February 18, 2021 Greetings! It has been a while since I’ve posted anything in my systems administration blog. In this post I will describe my process for building a newer version of the Mongo C driver for Enterprise Linux/CentOS 7. These steps were also performed on an Enterprise Linux 6 system, but this post will focus on EL7 solely. Last year I was asked by a developer to obtain a newer version of the Mongo C Driver for CentOS 7, as the one currently available in the EPEL repository, 1.3.6, does not support the most recent versions of MongoDB. I could not find a guide on the Internet for building an RPM of the newer version, so I was required to piece together a solution on my own. Originally I built the package on a build virtual machine; however, I later chose to use Docker, as the package requires several dependencies and I did not want to break anything on the build server. This example uses Docker, but it should work on a standard CentOS system as well. To begin, I obtained a spec file for the latest mongo-c-driver, for Fedora. At the time I used the FC34 mongo-c-driver-1.17.1-1 SRPM from Remi’s RPM repository and modified it to make it to work on CentOS 6 and 7. All credit goes to Remi Collet; I merely adapted this spec file for my own needs. Expand the below to see the example spec file: mongo-c-driver.spec # remirepo spec file for mongo-c-driver # # Copyright (c) 2015-2020 Remi Collet # License: CC-BY-SA # http://creativecommons.org/licenses/by-sa/4.0/ # %global gh_owner mongodb %global gh_project mongo-c-driver %global libname libmongoc %global libver 1.0 %global up_version 1.17.4 #global up_prever rc0 # disabled as require a MongoDB server %bcond_with tests Name: mongo-c-driver Summary: Client library written in C for MongoDB Version: %{up_version}%{?up_prever:~%{up_prever}} Release: 1%{?dist} # See THIRD_PARTY_NOTICES License: ASL 2.0 and ISC and MIT and zlib URL: https://github.com/%{gh_owner}/%{gh_project} Source0: https://github.com/%{gh_owner}/%{gh_project}/releases/download/%{up_version}%{?up_prever:-%{up_prever}}/%{gh_project}-%{up_version}%{?up_prever:-%{up_prever}}.tar.gz BuildRequires: cmake3 BuildRequires: gcc # pkg-config may pull compat-openssl10 BuildRequires: openssl-devel %if %{with tests} BuildRequires: mongodb-server BuildRequires: openssl %endif # From man pages Requires: %{name}-libs%{?_isa} = %{version}-%{release} Requires: libmongocrypt # Sub package removed Obsoletes: %{name}-tools < 1.3.0 Provides: %{name}-tools = %{version} Provides: %{name}-tools%{?_isa} = %{version} %description %{name} is a client library written in C for MongoDB. %package libs Summary: Shared libraries for %{name} %description libs This package contains the shared libraries for %{name}. %package devel Summary: Header files and development libraries for %{name} Requires: %{name}%{?_isa} = %{version}-%{release} Requires: pkgconfig Requires: pkgconfig(libzstd) Requires: libmongocrypt %description devel This package contains the header files and development libraries for %{name}. Documentation: http://mongoc.org/libmongoc/%{version}/ %package -n libbson Summary: Building, parsing, and iterating BSON documents # Modified (with bson allocator and some warning fixes and huge indentation # refactoring) jsonsl is bundled . # jsonsl upstream likes copylib approach and does not plan a release # . Provides: bundled(jsonsl) %description -n libbson This is a library providing useful routines related to building, parsing, and iterating BSON documents . %package -n libbson-devel Summary: Development files for %{name} Requires: libbson%{?_isa} = %{version}-%{release} Requires: pkgconfig %description -n libbson-devel This package contains libraries and header files needed for developing applications that use %{name}. Documentation: http://mongoc.org/libbson/%{version}/ %prep %setup -q -n %{gh_project}-%{up_version}%{?up_prever:-%{up_prever}} %build %cmake3 \ -DENABLE_BSON:STRING=ON \ -DENABLE_MONGOC:BOOL=ON \ -DENABLE_SHM_COUNTERS:BOOL=ON \ -DENABLE_SSL:STRING=OPENSSL \ -DENABLE_SASL:STRING=CYRUS \ -DENABLE_MONGODB_AWS_AUTH:STRING=ON \ -DENABLE_ICU:STRING=ON \ -DENABLE_AUTOMATIC_INIT_AND_CLEANUP:BOOL=OFF \ -DENABLE_CRYPTO_SYSTEM_PROFILE:BOOL=ON \ -DENABLE_MAN_PAGES:BOOL=ON \ -DENABLE_STATIC:STRING=OFF \ %if %{with tests} -DENABLE_TESTS:BOOL=ON \ %else -DENABLE_TESTS:BOOL=OFF \ %endif -DENABLE_EXAMPLES:BOOL=OFF \ -DENABLE_UNINSTALL:BOOL=OFF \ -DENABLE_CLIENT_SIDE_ENCRYPTION:BOOL=ON \ -S . %if 0%{?cmake_build:1} %cmake_build %else make %{?_smp_mflags} %endif %install %if 0%{?cmake_install:1} %cmake_install %else make install DESTDIR=%{buildroot} %endif : Static library rm -f %{buildroot}%{_libdir}/*.a rm -rf %{buildroot}%{_libdir}/cmake/*static* rm -rf %{buildroot}%{_libdir}/pkgconfig/*static* : Documentation rm -rf %{buildroot}%{_datadir}/%{name} %check ret=0 %if %{with tests} : Run a server mkdir dbtest mongod \ --journal \ --ipv6 \ --unixSocketPrefix /tmp \ --logpath $PWD/server.log \ --pidfilepath $PWD/server.pid \ --dbpath $PWD/dbtest \ --fork : Run the test suite export MONGOC_TEST_OFFLINE=on export MONGOC_TEST_SKIP_MOCK=on #export MONGOC_TEST_SKIP_SLOW=on make check || ret=1 : Cleanup [ -s server.pid ] && kill $(cat server.pid) %endif if grep -r static %{buildroot}%{_libdir}/cmake; then : cmake configuration file contain reference to static library ret=1 fi exit $ret %files %{_bindir}/mongoc-stat %files libs %{!?_licensedir:%global license %%doc} %license COPYING %license THIRD_PARTY_NOTICES %{_libdir}/%{libname}-%{libver}.so.* %files devel %doc src/%{libname}/examples %doc NEWS %{_includedir}/%{libname}-%{libver} %{_libdir}/%{libname}-%{libver}.so %{_libdir}/pkgconfig/%{libname}-*.pc %{_libdir}/cmake/%{libname}-%{libver} %{_libdir}/cmake/mongoc-%{libver} %{_mandir}/man3/mongoc* %files -n libbson %license COPYING %license THIRD_PARTY_NOTICES %{_libdir}/libbson*.so.* %files -n libbson-devel %doc src/libbson/examples %doc src/libbson/NEWS %{_includedir}/libbson-%{libver} %{_libdir}/libbson*.so %{_libdir}/cmake/libbson-%{libver} %{_libdir}/cmake/bson-%{libver} %{_libdir}/pkgconfig/libbson-*.pc %{_mandir}/man3/bson* I placed this spec file in a work directory, then downloaded the mongo-c-driver tarball from https://github.com/mongodb/mongo-c-driver/releases into the same work directory. I also created two Yum repo files for installing some of the dependencies for the build. These were libmongocrypt and MongoDB (I chose to install version 4.0): [libmongocrypt] name=libmongocrypt repository baseurl=https://libmongocrypt.s3.amazonaws.com/yum/redhat/$releasever/libmongocrypt/1.0/x86_64 gpgcheck=1 enabled=1 gpgkey=https://www.mongodb.org/static/pgp/libmongocrypt.asc [mongodb-org-4.0] name=MongoDB Repository baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.0/x86_64/ gpgcheck=1 enabled=1 gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc Next, I created a Dockerfile in the work directory that installs the required dependencies and builds the image. FROM centos:7 MAINTAINER Matt Ridpath matt@example.com COPY libmongocrypt.repo /etc/yum.repos.d COPY mongodb.repo /etc/yum.repos.d RUN yum install -y epel-release RUN yum install -y rpm-build icu libicu-devel python-sphinx python2-sphinx snappy cmake3 libzstd-devel libmongocrypt mongodb-org-server gcc openssl-devel cyrus-sasl-devel WORKDIR /root RUN mkdir -p rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} COPY mongo-c-driver-*.tar.gz rpmbuild/SOURCES COPY mongo-c-driver.spec rpmbuild/SPECS Now the container image can be built: sudo docker build --pull -t="matt/mongo-c-driver" . Next, I created a directory for the container to mount as a pass-through volume, so that it has a location to copy the finished RPMs into. I chose to create an “artifacts” directory within my work directory, as this task would later be turned into a Jenkins job. You can chose another location, however. The option follows the format of -v <local_dir>:<container_dir>. Once the directory had been created, I ran the container to build the RPMs: sudo docker run --rm -v $HOME/mongo-c-driver/artifacts:/artifacts matt/mongo-c-driver /bin/bash -c "cd rpmbuild/SPECS && rpmbuild -ba mongo-c-driver.spec && cp ../RPMS/x86_64/*.rpm /artifacts" If the RPM builds successfully, the RPMs should be dropped into the specified local directory in the -v option and made available for installation. matt@docker:~$ ls mongo-c-driver/artifacts/ libbson-1.17.4-1.el7.x86_64.rpm libbson-devel-1.17.4-1.el7.x86_64.rpm mongo-c-driver-1.17.4-1.el7.x86_64.rpm mongo-c-driver-debuginfo-1.17.4-1.el7.x86_64.rpm mongo-c-driver-devel-1.17.4-1.el7.x86_64.rpm mongo-c-driver-libs-1.17.4-1.el7.x86_64.rpm In a future post, I will show how I turned this task into a Jenkins job, so that one can build these packages regularly as new versions are released. Finally, a disclaimer: I’m not an expert on Docker or building RPMs. Someone else more than likely has a better solution for achieving this end result. Rather, I would merely like to share how I arrived at a solution for building these packages and hope it will save someone else the trial and error I went through. Credits:1) Remi Collet for providing the spec file to modify for this task: https://rpms.remirepo.net/.2) mongo-c-driver contributors and authors for providing the source tarball: https://github.com/mongodb/mongo-c-driver.3) This guide for providing an example of how to build RPMs with Docker: http://saule1508.github.io/build-rpm-with-docker/.