RPM: Using the rpm command (Tips and tricks)

From Define Wiki
Revision as of 13:29, 7 October 2020 by Antony c (talk | contribs) (added long section on creation of a single package rpm)
Jump to navigation Jump to search

Extract Scripts

rpm -qp --scripts my_package.rpm > ListOfScripts

Force remove/ uninstall

rpm -e --nodeps

Extract Files

  • Not installing
rpm2cpio my_package.rpm | cpio -idmv

What RPM does a file belong to

rpm -qif /path/to/file

What files does an RPM provide

#If RPM is installed
rpm -ql rpm-pacakge-name

# include file info
rpm -qil rpm-package-name

#If RPM is not installed
rpm -qpl ./rpm-package-name
rpm -qpil ./rpm-package-name

Create an RPM

1. I recompiled hdf5 and put it under /shared/hdf5-1.8.7.gnu/ :

[root@mel1 ~]# ls /shared/hdf5-1.8.7.gnu/
bin  include  lib  share

2. I created a dummy spec file that will just package what’s under this directory:

Summary: my own package
Name: hdf
Version: 1.2.3.4
Release: 1          
License: my_license
Group: Applications/System


%description
Brief description of software package.

%prep

%build

%install

%clean

%files
%defattr(-,root,root)


/shared/hdf5-1.8.7.gnu/*


%changelog

3. Then I can build the binary rpm:

rpmbuild -bb ./testhdf.spec

4. The binary rpm will be then available here:

[root@mel1 ~]# ls /usr/src/redhat/RPMS/x86_64/hdf-1.2.3.4-1.x86_64.rpm
/usr/src/redhat/RPMS/x86_64/hdf-1.2.3.4-1.x86_64.rpm                
[root@mel1 ~]# rpm -qpi /usr/src/redhat/RPMS/x86_64/hdf-1.2.3.4-1.x86_64.rpm
Name        : hdf                          Relocations: (not relocatable)                  
Version     : 1.2.3.4                           Vendor: (none)
Release     : 1                             Build Date: Mon 18 Jul 2011 11:53:14 AM EDT
Install Date: (not installed)               Build Host: mel1.lsf.platform.com
Group       : Applications/System           Source RPM: hdf-1.2.3.4-1.src.rpm
Size        : 20293890                         License: my_license
Signature   : (none)
Summary     : my own package
Description :
Brief description of software package.
[root@mel1 ~]# rpm -qp --provides /usr/src/redhat/RPMS/x86_64/hdf-1.2.3.4-1.x86_64.rpm
libhdf5.so.7()(64bit)                
libhdf5_cpp.so.7()(64bit)
libhdf5_fortran.so.7()(64bit)
libhdf5_hl.so.7()(64bit)
libhdf5_hl_cpp.so.7()(64bit)
libhdf5hl_fortran.so.7()(64bit)
hdf = 1.2.3.4-1
[root@mel1 ~]# rpm -qpl /usr/src/redhat/RPMS/x86_64/hdf-1.2.3.4-1.x86_64.rpm
/shared/hdf5-1.8.7.gnu/bin
/shared/hdf5-1.8.7.gnu/bin/gif2h5
/shared/hdf5-1.8.7.gnu/bin/h52gif
/shared/hdf5-1.8.7.gnu/bin/h5c++
/shared/hdf5-1.8.7.gnu/bin/h5cc
/shared/hdf5-1.8.7.gnu/bin/h5copy
/shared/hdf5-1.8.7.gnu/bin/h5debug
/shared/hdf5-1.8.7.gnu/bin/h5diff
/shared/hdf5-1.8.7.gnu/bin/h5dump
/shared/hdf5-1.8.7.gnu/bin/h5fc
/shared/hdf5-1.8.7.gnu/bin/h5import
/shared/hdf5-1.8.7.gnu/bin/h5jam
/shared/hdf5-1.8.7.gnu/bin/h5ls
/shared/hdf5-1.8.7.gnu/bin/h5mkgrp
/shared/hdf5-1.8.7.gnu/bin/h5perf_serial
/shared/hdf5-1.8.7.gnu/bin/h5redeploy
/shared/hdf5-1.8.7.gnu/bin/h5repack
/shared/hdf5-1.8.7.gnu/bin/h5repart
/shared/hdf5-1.8.7.gnu/bin/h5stat
....
...

Create a single package RPM from a git repo

Prep work

create a RPMBUILD structure in /home

[antony@controller SOURCES]$ cd ~/
[antony@controller SOURCES]$ mkdir rpmbuild
[antony@controller SOURCES]$ cd rpmbuild
[antony@controller SOURCES]$ mkdir BUILD BUILDROOT RPMS SOURCES SPECS SRPMS

you should see something like this:

[antony@controller rpmbuild]$ ls
BUILD  BUILDROOT  RPMS  SOURCES  SPECS  SRPMS

Clone your repo into the sources folder and make sure you have fected the tags

We will be using sensu-go

cd into the rpmbuild/sfolder and clone the repo like so:

 
[antony@controller SOURCES]$ cd ~/rpmbuild/SOURCES
[antony@controller SOURCES]$ git clone https://github.com/sensu/sensu-go.git
Cloning into 'sensu-go'...
remote: Enumerating objects: 199, done.
remote: Counting objects: 100% (199/199), done.
remote: Compressing objects: 100% (138/138), done.
remote: Total 54523 (delta 82), reused 102 (delta 46), pack-reused 54324
Receiving objects: 100% (54523/54523), 167.63 MiB | 17.41 MiB/s, done.
Resolving deltas: 100% (32605/32605), done.

now cd in and fetch the tags and checkout to version we want (we will use 6.0)

[antony@controller SOURCES]$ cd sensu-go/
[antony@controller sensu-go]$ git fetch --tags --all

you can check the branch details with git branch -a like so (output is snipped)

[antony@controller sensu-go]$ git branch -a
* master
---
  remotes/origin/parallel-tests
  remotes/origin/releas/5.2
  remotes/origin/release/5.20
  remotes/origin/release/5.21
  remotes/origin/release/6.0
  remotes/origin/release/6.1
---
[antony@controller sensu-go]$ git checkout release/6.0
Branch release/6.0 set up to track remote branch release/6.0 from origin.
Switched to a new branch 'release/6.0'

Introduction to a SPEC file

The Following show all the parts of a spec file for the sensu-go backend with the complex stuff removed we will add this later

###############################################################################
# Spec file for sensu-go backend
###############################################################################
#
Summary: Sensu Go Backend
Name: sensu-go-backend
Version: 6.0
Release: 1
License: Sensu Open Source Licence
URL: https://github.com/sensu/sensu-go
Group: System
Packager: Antony Cleave
Requires: bash
BuildRoot: %{buildroot}/%{name}-%{version}
BuildRequires: golang,git,curl
%Description
Sensu is an open source monitoring tool for ephemeral infrastructure and distributed applications. It is an agent based monitoring system with built-in auto-discovery, making it very well-suited for cloud environments. Sensu uses service checks to monitor service health and collect telemetry data. It also has a number of well defined APIs for configuration, external data input, and to provide access to Sensu's data. Sensu is extremely extensible and is commonly referred to as "the monitoring router".
%prep

# this part is a bash script to prepare your build we will use this to checkout the the right version from the repo before we start

%build

# this part is a bash script to build the code there are macros to help with a traditional configure; make; make install type package

%files

# this section lists all of the files that will be packged in our RPM

%post

# this section lists all of the parts that will be done after copying the file

%preun

# this section is run before the rpm is uninstalled

Hopefully a lot of the above is self explanatory from the content. A brief note on the BuildRoot and BuildRequires. The BuildRoot is like a chroot. It in a traditional install process we would set the build prefix to the path set in the BuildRoot variable in the configure step to $RPM_BUILD_ROOT. We SET BuildRoot in the spec file using the rpmbuild macros %{buildroot} which in our layout above is ~/rpmbuild/BUILDROOT. The %{name} expands to what we set in the Name field (sensu-go-backend) and the version expands to what we set in the Version field (6.0) to set the BuildRoot to ~/rpmbuild/BUILDROOT/sensu-go-backend/6.0/. The BuildRequires are rpm packages that need to be installed for the rpmbuild command to work. The Requires at the package dependencies for the runtime.

As part of the following example we will fill out the empty sections:

The %prep section we will expand to cd into the git repo, pull any changes and checkout the version we want.

The %build section we will expand to copy the checked out git repo into a folder in the rpmbuild/BUILD (%{_builddir}) and perform the build using go to make the sesu-go-backend binary and then populate the BuildRoot folder using the binary. There is no makefile and no make install for sensu so we must manually create the folders we need to exist in the BuildRoot.

The %files section contains a list of paths with optional permissions that need to be created by the RPM. If you have any files that exist in the BuildRoot that are NOT in the file list then the RPM build process will fail.

The %post section we will expand to link the systemd unit into the /etc/systemd/system folder if it is NOT already in place. We will also copy the example config file into /etc/sensu/backend.yml if there is nothing present.

The %preun section will not actually be used it's just here to document it's existence. It could be used to stop the service before we uninstall the files as an example.

Populating the %prep section

Here we could have more complex logic to see if the repo exists, clone it and then checkout the version... But you only need to clone the repo ONCE so why bother. We only need to cd in the folder, pull any changes (we should never need to worry about local changes here complicating the process with merging) and then checkout the required version.

paste in:

cd %{_sourcedir}/sensu-go
git pull
git checkout release/%{version}

in your repo you may need to use a different checkout command to be able to do something similar

We can now test this cd into your rpmbuild folder and build the specfile, mine is called sensu-go-backend-6.0-antony.x86_64.spec:

[antony@controller sensu-go]$ cd ~/rpmbuild
[antony@controller rpmbuild]$ rpmbuild -bb SPECS/sensu-go-backend-6.0-antony.x86_64.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.afPOFR
+ umask 022
+ cd /trinity/home/antony/rpmbuild/BUILD
+ cd /trinity/home/antony/rpmbuild/SOURCES/sensu-go
+ git pull
remote: Enumerating objects: 36, done.
remote: Counting objects: 100% (36/36), done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 36 (delta 24), reused 36 (delta 24), pack-reused 0
Unpacking objects: 100% (36/36), done.
From https://github.com/sensu/sensu-go
 + f446f55...ce5f4d0 bugfix/silenced-expiry -> origin/bugfix/silenced-expiry  (forced update)
   c87d385..ce5b2f6  feature/prom-build-info -> origin/feature/prom-build-info
 * [new branch]      hfraley/readme-webui-install -> origin/hfraley/readme-webui-install
 * [new branch]      jdp/created-by-backend-api -> origin/jdp/created-by-backend-api
 * [new branch]      jdp/entities-suggest-workaround -> origin/jdp/entities-suggest-workaround
 * [new tag]         api/core/v2.4.0-alpha -> api/core/v2.4.0-alpha
Already up-to-date.
+ git checkout release/6.0
Branch release/6.0 set up to track remote branch release/6.0 from origin.
Switched to a new branch 'release/6.0'
+ exit 0
Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.2zyGIC
+ umask 022
+ cd /trinity/home/antony/rpmbuild/BUILD
+ exit 0
Processing files: sensu-go-backend-6.0-1.x86_64
Provides: sensu-go-backend = 6.0-1 sensu-go-backend(x86-64) = 6.0-1
Requires(interp): /bin/sh /bin/sh
Requires(rpmlib): rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1 rpmlib(CompressedFileNames) <= 3.0.4-1
Requires(post): /bin/sh
Requires(preun): /bin/sh
Checking for unpackaged file(s): /usr/lib/rpm/check-files /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64
Wrote: /trinity/home/antony/rpmbuild/RPMS/x86_64/sensu-go-backend-6.0-1.x86_64.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.Coj2T8
+ umask 022
+ cd /trinity/home/antony/rpmbuild/BUILD
+ /usr/bin/rm -rf /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64
+ exit 0

Populating the %build section

As mentioned now we have "prepared" our sources. Now we can build it. To make sure that we are building a clean version I like to clear out any previous folders first (but this is not necessary as there is a clean function if you want to look into this). To facilitate this I do it in /tmp/buildroot/%{name}-%{version} as only I should be messing around in here.

Also as mentioned previously the sensu-go build instructions do not use a makefile or anything to ease installation as they want you to pay for support and to use the enterprise edition. This makes it an excellent example for documenting how to populate a buildroot by hand as we have no other option.

%build
# clean any previous build and make the build folder in rpm build dir
rm -rf %{_builddir}/%{name}-%{version}
mkdir -p %{_builddir}/%{name}-%{version}
 
# populate the build folder from the prepared sources folder
cp -a %{_sourcedir}/sensu-go %{_builddir}/%{name}-%{version}
 
# cd into the folder to build in
cd %{_builddir}/%{name}-%{version}/sensu-go

# build the sensu-backend executable
go build -o sensu-backend ./cmd/sensu-backend

# create /usr/sbin/ in the BuildRoot path
mkdir -p $RPM_BUILD_ROOT/usr/sbin

# move the built binary into the created $RPM_BUILD_ROOT/usr/sbin/
mv sensu-backend $RPM_BUILD_ROOT/usr/sbin/

# create remaining required paths
mkdir -p $RPM_BUILD_ROOT/var/cache/sensu/
mkdir -p $RPM_BUILD_ROOT/var/cache/sensu/sensu-backend
mkdir -p $RPM_BUILD_ROOT/var/lib/sensu/
mkdir -p $RPM_BUILD_ROOT/var/lib/sensu/sensu-backend
mkdir -p $RPM_BUILD_ROOT/var/log/sensu
mkdir -p $RPM_BUILD_ROOT/var/run/sensu
mkdir -p $RPM_BUILD_ROOT/etc/sensu
mkdir -p $RPM_BUILD_ROOT/usr/share/doc/sensu-go-backend-%{version}
mkdir -p $RPM_BUILD_ROOT/lib/systemd/system/

#copy the LICENSE file into RPM_BUILD_ROOT/usr/share/doc/sensu-go-backend-%{version}/LICENSE.txt
cp LICENSE $RPM_BUILD_ROOT/usr/share/doc/sensu-go-backend-%{version}/LICENSE.txt

# the source doesn't include an example config for now curl it from the website, this isn't a great idea as over time it might not be compatible with the version we are creating a the RPM for but it works for now.
curl -L https://docs.sensu.io/sensu-go/latest/files/backend.yml -o $RPM_BUILD_ROOT/usr/share/doc/sensu-go-backend-%{version}/backend.yml.example

# create a sensu-go-backend.service in the right location This time use a HEREDOC which we probably should use above
cat <<'EOF' >$RPM_BUILD_ROOT/lib/systemd/system/sensu-go-backend.service
[Unit]
Description=The Sensu Backend service.
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=sensu
Group=sensu
# Load env vars from /etc/default/ and /etc/sysconfig/ if they exist.
# Prefixing the path with '-' makes it try to load, but if the file doesn't
# exist, it continues onward.
EnvironmentFile=-/etc/default/sensu-backend
EnvironmentFile=-/etc/sysconfig/sensu-backend
LimitNOFILE=65535
ExecStart=/usr/sbin/sensu-backend start -c /etc/sensu/backend.yml
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
WorkingDirectory=/

[Install]
WantedBy=multi-user.target
EOF
cd $RPM_BUILD_ROOT

Now we can try an test again. This time it's going to fail. If you are asking "Why?" at this point you were skimming too fast earlier.

if you do test you should see the following:

[antony@controller rpmbuild]$ rpmbuild -bb SPECS/sensu-go-backend-6.0-antony.x86_64.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.sqQjSS
+ umask 022
+ cd /trinity/home/antony/rpmbuild/BUILD
+ cd /trinity/home/antony/rpmbuild/SOURCES/sensu-go
+ git pull
Already up-to-date.
+ git checkout release/6.0
Already on 'release/6.0'
+ exit 0
Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.AE2u6X
+ umask 022
+ cd /trinity/home/antony/rpmbuild/BUILD
+ rm -rf /trinity/home/antony/rpmbuild/BUILD/sensu-go-backend-6.0
+ mkdir -p /trinity/home/antony/rpmbuild/BUILD/sensu-go-backend-6.0
+ cp -a /trinity/home/antony/rpmbuild/SOURCES/sensu-go /trinity/home/antony/rpmbuild/BUILD/sensu-go-backend-6.0
+ cd /trinity/home/antony/rpmbuild/BUILD/sensu-go-backend-6.0/sensu-go
+ go build -o sensu-backend ./cmd/sensu-backend
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/sbin
+ mv sensu-backend /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/sbin/
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/cache/sensu/
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/cache/sensu/sensu-backend
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/lib/sensu/
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/lib/sensu/sensu-backend
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/log/sensu
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/run/sensu
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/etc/sensu
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/share/doc/sensu-go-backend-6.0
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/lib/systemd/system/
+ cp LICENSE /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/share/doc/sensu-go-backend-6.0/LICENSE.txt
+ curl -L https://docs.sensu.io/sensu-go/latest/files/backend.yml -o /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/share/doc/sensu-go-backend-6.0/backend.yml.example
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1768  100  1768    0     0   3201      0 --:--:-- --:--:-- --:--:--  3202
+ cat
+ cd /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64
+ exit 0
Processing files: sensu-go-backend-6.0-1.x86_64
Provides: sensu-go-backend = 6.0-1 sensu-go-backend(x86-64) = 6.0-1
Requires(interp): /bin/sh /bin/sh
Requires(rpmlib): rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1 rpmlib(CompressedFileNames) <= 3.0.4-1
Requires(post): /bin/sh
Requires(preun): /bin/sh
Checking for unpackaged file(s): /usr/lib/rpm/check-files /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64
error: Installed (but unpackaged) file(s) found:
   /lib/systemd/system/sensu-go-backend.service
   /usr/sbin/sensu-backend
   /usr/share/doc/sensu-go-backend-6.0/LICENSE.txt
   /usr/share/doc/sensu-go-backend-6.0/backend.yml.example


RPM build errors:
    Installed (but unpackaged) file(s) found:
   /lib/systemd/system/sensu-go-backend.service
   /usr/sbin/sensu-backend
   /usr/share/doc/sensu-go-backend-6.0/LICENSE.txt
   /usr/share/doc/sensu-go-backend-6.0/backend.yml.example

The reason it is failing is because there are files we have created in the BuildRoot path which are not mentioned in the %files section because we haven't populated it yet. Lets do it now!

Populating the %files section

This section details all the files that make up the package. As we have found out it needs to mention all the files in the BuildRoot path we can do this explicilty or allow it to recurse down explicit is safer and necessary when ownership and permissions need to be different. For sensu-go we need some files to be owned by the sensu user (no we haven't created this yet and we actually won't in this example for reasons we will get to later) but not ALL the files are owned by the sensu user... and we want some folders to have different permissions from default so we shall go explicit. For an example of where we don't care. . . see Using the rpm command (Tips and tricks):Create an RPM

paste the following into the %files section note how we use the %attr(<mode>,<user>,<group>) macro to set ownership and permissions

%files
/usr/sbin/sensu-backend
/etc/sensu
%attr(0755,sensu,sensu) /var/cache/sensu/
%attr(0750,sensu,sensu) /var/cache/sensu/sensu-backend
%attr(0755,sensu,sensu) /var/lib/sensu/
%attr(0750,sensu,sensu) /var/lib/sensu/sensu-backend
%attr(0750,sensu,sensu) /var/log/sensu
%attr(0750,sensu,sensu) /var/run/sensu
/usr/share/doc/sensu-go-backend-%{version}/LICENSE.txt
/usr/share/doc/sensu-go-backend-%{version}/backend.yml.example
/lib/systemd/system/sensu-go-backend.service

We now have a '"working"' RPM if we build it. Lets try building it again!

[antony@controller rpmbuild]$ rpmbuild -bb SPECS/sensu-go-backend-6.0-antony.x86_64.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.baUBFs
+ umask 022
+ cd /trinity/home/antony/rpmbuild/BUILD
+ cd /trinity/home/antony/rpmbuild/SOURCES/sensu-go
+ git pull
Already up-to-date.
+ git checkout release/6.0
Already on 'release/6.0'
+ exit 0
Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.PltILU
+ umask 022
+ cd /trinity/home/antony/rpmbuild/BUILD
+ rm -rf /trinity/home/antony/rpmbuild/BUILD/sensu-go-backend-6.0
+ mkdir -p /trinity/home/antony/rpmbuild/BUILD/sensu-go-backend-6.0
+ cp -a /trinity/home/antony/rpmbuild/SOURCES/sensu-go /trinity/home/antony/rpmbuild/BUILD/sensu-go-backend-6.0
+ cd /trinity/home/antony/rpmbuild/BUILD/sensu-go-backend-6.0/sensu-go
+ go build -o sensu-backend ./cmd/sensu-backend
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/sbin
+ mv sensu-backend /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/sbin/
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/cache/sensu/
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/cache/sensu/sensu-backend
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/lib/sensu/
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/lib/sensu/sensu-backend
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/log/sensu
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/run/sensu
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/etc/sensu
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/share/doc/sensu-go-backend-6.0
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/lib/systemd/system/
+ cp LICENSE /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/share/doc/sensu-go-backend-6.0/LICENSE.txt
+ curl -L https://docs.sensu.io/sensu-go/latest/files/backend.yml -o /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/share/doc/sensu-go-backend-6.0/backend.yml.example
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1768  100  1768    0     0   3252      0 --:--:-- --:--:-- --:--:--  3250
+ cat
+ cd /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64
+ exit 0
Processing files: sensu-go-backend-6.0-1.x86_64
warning: File listed twice: /var/cache/sensu/sensu-backend
warning: File listed twice: /var/lib/sensu/sensu-backend
Provides: sensu-go-backend = 6.0-1 sensu-go-backend(x86-64) = 6.0-1
Requires(interp): /bin/sh /bin/sh
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires(post): /bin/sh
Requires(preun): /bin/sh
Requires: libc.so.6()(64bit) libc.so.6(GLIBC_2.2.5)(64bit) libpthread.so.0()(64bit) libpthread.so.0(GLIBC_2.2.5)(64bit) libpthread.so.0(GLIBC_2.3.2)(64bit)
Checking for unpackaged file(s): /usr/lib/rpm/check-files /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64
Wrote: /trinity/home/antony/rpmbuild/RPMS/x86_64/sensu-go-backend-6.0-1.x86_64.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.fWhJhK
+ umask 022
+ cd /trinity/home/antony/rpmbuild/BUILD
+ /usr/bin/rm -rf /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64
+ exit 0

So why is the RPM only '"working"'?

sensu-go is a systemd service that requires a config file. It is lacking both a service file in /etc/systemd/system/ (we put it into /lib/systemd/system/sensu-go-backend.service as recommended) and the config file (we put an example into (/usr/share/doc/sensu-go-backend-%{version}/backend.yml.example also as recommended). Lets Fix both problems now!

Populating the %post section

So if you are anything like me you are wondering why didn't we just put the config file and the systemd unit file in the right places to start with. The answer is the same for both. It is entirely possible that you want to upgrade the rpm and NOT touch these files because you have customised the config or the systemd unit. This is why we put them elsewhere. It is the purpose of the post section to perform checks like this after the files have been copied and before the rpm install process ends.


paste in the following %post section:

%post
[ ! -e /etc/sensu/backend.yml ] && cp /usr/share/doc/sensu-go-backend-%{version}/backend.yml.example /etc/sensu/backend.yml
[ ! -e /etc/systemd/system/sensu-go-backend.service ] && ln -s /lib/systemd/system/sensu-go-backend.service /etc/systemd/system/sensu-go-backend.service

the first checks to see if a file entry exists at /etc/sensu/backend.yml and if not then it will copy the example config we are installing into /usr/share/doc/sensu-go-backend-%{version}/backend.yml.example the second checks to see if a file entry exists at /lib/systemd/system/sensu-go-backend.service and if not then it will create a link in /etc/systemd/system/sensu-go-backend.service pointing to /lib/systemd/system/sensu-go-backend.service

if you build it now it will give you an RPM you can install, and then expect to work . . . IF you have a sensu user that is. Lets build it!

[antony@controller rpmbuild]$ rpmbuild -bb SPECS/sensu-go-backend-6.0-antony.x86_64.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.obECwx
+ umask 022
+ cd /trinity/home/antony/rpmbuild/BUILD
+ cd /trinity/home/antony/rpmbuild/SOURCES/sensu-go
+ git pull
Already up-to-date.
+ git checkout release/6.0
Already on 'release/6.0'
+ exit 0
Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.02eWxf
+ umask 022
+ cd /trinity/home/antony/rpmbuild/BUILD
+ rm -rf /trinity/home/antony/rpmbuild/BUILD/sensu-go-backend-6.0
+ mkdir -p /trinity/home/antony/rpmbuild/BUILD/sensu-go-backend-6.0
+ cp -a /trinity/home/antony/rpmbuild/SOURCES/sensu-go /trinity/home/antony/rpmbuild/BUILD/sensu-go-backend-6.0
+ cd /trinity/home/antony/rpmbuild/BUILD/sensu-go-backend-6.0/sensu-go
+ go build -o sensu-backend ./cmd/sensu-backend
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/sbin
+ mv sensu-backend /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/sbin/
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/cache/sensu/
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/cache/sensu/sensu-backend
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/lib/sensu/
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/lib/sensu/sensu-backend
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/log/sensu
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/var/run/sensu
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/etc/sensu
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/share/doc/sensu-go-backend-6.0
+ mkdir -p /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/lib/systemd/system/
+ cp LICENSE /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/share/doc/sensu-go-backend-6.0/LICENSE.txt
+ curl -L https://docs.sensu.io/sensu-go/latest/files/backend.yml -o /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64/usr/share/doc/sensu-go-backend-6.0/backend.yml.example
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1768  100  1768    0     0   3135      0 --:--:-- --:--:-- --:--:--  3140
+ cat
+ cd /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64
+ exit 0
Processing files: sensu-go-backend-6.0-1.x86_64
warning: File listed twice: /var/cache/sensu/sensu-backend
warning: File listed twice: /var/lib/sensu/sensu-backend
Provides: sensu-go-backend = 6.0-1 sensu-go-backend(x86-64) = 6.0-1
Requires(interp): /bin/sh /bin/sh /bin/sh
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires(post): /bin/sh
Requires(preun): /bin/sh
Requires(postun): /bin/sh
Requires: libc.so.6()(64bit) libc.so.6(GLIBC_2.2.5)(64bit) libpthread.so.0()(64bit) libpthread.so.0(GLIBC_2.2.5)(64bit) libpthread.so.0(GLIBC_2.3.2)(64bit)
Checking for unpackaged file(s): /usr/lib/rpm/check-files /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64
Wrote: /trinity/home/antony/rpmbuild/RPMS/x86_64/sensu-go-backend-6.0-1.x86_64.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.oqGciC
+ umask 022
+ cd /trinity/home/antony/rpmbuild/BUILD
+ /usr/bin/rm -rf /trinity/home/antony/rpmbuild/BUILDROOT/sensu-go-backend-6.0-1.x86_64
+ exit 0

Testing the RPM

and install it (note I'm root in the following example)

trix12-test 13:00:22 [root@controller ~]# yum install ~antony/rpmbuild/RPMS/x86_64/sensu-go-backend-6.0-1.x86_64.rpm
Loaded plugins: fastestmirror, versionlock
Examining /trinity/home/antony/rpmbuild/RPMS/x86_64/sensu-go-backend-6.0-1.x86_64.rpm: sensu-go-backend-6.0-1.x86_64
Marking /trinity/home/antony/rpmbuild/RPMS/x86_64/sensu-go-backend-6.0-1.x86_64.rpm to be installed
Resolving Dependencies
--> Running transaction check
---> Package sensu-go-backend.x86_64 0:6.0-1 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

=============================================================================================================================================================================================================================================
 Package                                                    Arch                                             Version                                          Repository                                                                Size
=============================================================================================================================================================================================================================================
Installing:
 sensu-go-backend                                           x86_64                                           6.0-1                                            /sensu-go-backend-6.0-1.x86_64                                            43 M

Transaction Summary
=============================================================================================================================================================================================================================================
Install  1 Package

Total size: 43 M
Installed size: 43 M
Is this ok [y/d/N]: y
Downloading packages:
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : sensu-go-backend-6.0-1.x86_64                                                                                                                                                                                             1/1
  Verifying  : sensu-go-backend-6.0-1.x86_64                                                                                                                                                                                             1/1

Installed:
  sensu-go-backend.x86_64 0:6.0-1

Complete!




trix12-test 13:00:32 [root@controller ~]# ll /etc/systemd/system/ | grep sensu
lrwxrwxrwx  1 root root   44 Oct  7 13:00 sensu-go-backend.service -> /lib/systemd/system/sensu-go-backend.service
trix12-test 13:00:35 [root@controller ~]# systemctl start sensu-go-backend
trix12-test 13:00:47 [root@controller ~]# systemctl status sensu-go-backend
● sensu-go-backend.service - The Sensu Backend service.
   Loaded: loaded (/lib/systemd/system/sensu-go-backend.service; linked; vendor preset: disabled)
   Active: active (running) since Wed 2020-10-07 13:00:51 UTC; 1s ago
 Main PID: 21485 (sensu-backend)
   CGroup: /system.slice/sensu-go-backend.service
           └─21485 /usr/sbin/sensu-backend start -c /etc/sensu/backend.yml

Oct 07 13:00:53 controller sensu-backend[21485]: {"component":"apid","level":"info","msg":"starting apid on address: [::]:8080","time":"2020-10-07T13:00:53Z"}
Oct 07 13:00:53 controller sensu-backend[21485]: {"component":"backend","level":"info","msg":"shutting down backend_id_getter","time":"2020-10-07T13:00:53Z"}
Oct 07 13:00:53 controller sensu-backend[21485]: {"component":"backend","level":"info","msg":"shutting down message_bus","time":"2020-10-07T13:00:53Z"}
Oct 07 13:00:53 controller sensu-backend[21485]: {"component":"backend","level":"info","msg":"shutting down pipelined","time":"2020-10-07T13:00:53Z"}
Oct 07 13:00:53 controller sensu-backend[21485]: {"component":"backend","level":"info","msg":"shutting down eventd","time":"2020-10-07T13:00:53Z"}
Oct 07 13:00:53 controller sensu-backend[21485]: {"component":"eventd","level":"info","msg":"shutting down eventd","time":"2020-10-07T13:00:53Z"}
Oct 07 13:00:53 controller sensu-backend[21485]: {"component":"backend","level":"info","msg":"shutting down schedulerd","time":"2020-10-07T13:00:53Z"}
Oct 07 13:00:53 controller sensu-backend[21485]: {"component":"backend","level":"info","msg":"shutting down agentd","time":"2020-10-07T13:00:53Z"}
Oct 07 13:00:53 controller sensu-backend[21485]: {"component":"store","key":"/sensu.io/checks/","level":"debug","msg":"stopping the watcher","time":"2020-10-07T13:00:53Z"}
Oct 07 13:00:53 controller sensu-backend[21485]: {"component":"agentd","level":"warning","msg":"shutting down entity config watcher","time":"2020-10-07T13:00:53Z"}
trix12-test 13:00:53 [root@controller ~]#

Final Comments

now we don't create the user here, Why? because there are other parts of sensu-go that also need the user created, they also require the same paths to exist with the same permissions. . . RPMs do not allow different packages to control the same files so what are we to do? Now is the time for subpackages or multiple RPMs created by the same SPEC file to install different parts of the same build for selective deployment. Lets start a new topic to handle the creation of the following multipackage RPM group. sensu-go-backend, sensu-go-agent, sensu-go-sensuctl which all depend on the created sensu-go-common... and gues where the user creation goes? No you moron, it's not in sensu-go-sensuctl.

Complete SPEC file

###############################################################################
# Spec file for sensu-go backend
################################################################################
#
Summary: Sensu Go Backend

Summary: Sensu Go Backend
Name: sensu-go-backend
Version: 6.0
Release: 1
License: Sensu Open Source Licence
URL: https://github.com/sensu/sensu-go
Group: System
Packager: Antony Cleave
Requires: bash
BuildRoot: %{buildroot}/%{name}-%{version}
BuildRequires: golang,git,curl
%Description
Sensu is an open source monitoring tool for ephemeral infrastructure and distributed applications. It is an agent based monitoring system with built-in auto-discovery, making it very well-suited for cloud environments. Sensu uses service checks to monitor service health and collect telemetry data. It also has a number of well defined APIs for configuration, external data input, and to provide access to Sensu's data. Sensu is extremely extensible and is commonly referred to as "the monitoring router".
%prep
cd %{_sourcedir}/sensu-go
git pull
git checkout release/%{version}
 
%build
# clean any previous build and make the build folder in rpm build dir
rm -rf %{_builddir}/%{name}-%{version}
mkdir -p %{_builddir}/%{name}-%{version}
 
# populate the build folder from the prepared sources folder
cp -a %{_sourcedir}/sensu-go %{_builddir}/%{name}-%{version}
 
# cd into the folder to build in
cd %{_builddir}/%{name}-%{version}/sensu-go
 
# build the sensu-backend executable
go build -o sensu-backend ./cmd/sensu-backend
 
# create /usr/sbin/ in the BuildRoot path
mkdir -p $RPM_BUILD_ROOT/usr/sbin
 
# move the built binary into the created $RPM_BUILD_ROOT/usr/sbin/
mv sensu-backend $RPM_BUILD_ROOT/usr/sbin/
 
# create remaining required paths
mkdir -p $RPM_BUILD_ROOT/var/cache/sensu/
mkdir -p $RPM_BUILD_ROOT/var/cache/sensu/sensu-backend
mkdir -p $RPM_BUILD_ROOT/var/lib/sensu/
mkdir -p $RPM_BUILD_ROOT/var/lib/sensu/sensu-backend
mkdir -p $RPM_BUILD_ROOT/var/log/sensu
mkdir -p $RPM_BUILD_ROOT/var/run/sensu
mkdir -p $RPM_BUILD_ROOT/etc/sensu
mkdir -p $RPM_BUILD_ROOT/usr/share/doc/sensu-go-backend-%{version}
mkdir -p $RPM_BUILD_ROOT/lib/systemd/system/
 
#copy the LICENSE file into RPM_BUILD_ROOT/usr/share/doc/sensu-go-backend-%{version}/LICENSE.txt
cp LICENSE $RPM_BUILD_ROOT/usr/share/doc/sensu-go-backend-%{version}/LICENSE.txt
 
# the source doesn't include an example config for now curl it from the website, this isn't a great idea as over time it might not be compatible with the version we are creating a the RPM for but it works for now.
curl -L https://docs.sensu.io/sensu-go/latest/files/backend.yml -o $RPM_BUILD_ROOT/usr/share/doc/sensu-go-backend-%{version}/backend.yml.example
 
# create a sensu-go-backend.service in the right location This time use a HEREDOC which we probably should use above
cat <<'EOF' >$RPM_BUILD_ROOT/lib/systemd/system/sensu-go-backend.service
[Unit]
Description=The Sensu Backend service.
After=network-online.target
Wants=network-online.target
 
[Service]
Type=simple
User=sensu
Group=sensu
# Load env vars from /etc/default/ and /etc/sysconfig/ if they exist.
# Prefixing the path with '-' makes it try to load, but if the file doesn't
# exist, it continues onward.
EnvironmentFile=-/etc/default/sensu-backend
EnvironmentFile=-/etc/sysconfig/sensu-backend
LimitNOFILE=65535
ExecStart=/usr/sbin/sensu-backend start -c /etc/sensu/backend.yml
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
WorkingDirectory=/
 
[Install]
WantedBy=multi-user.target
EOF
cd $RPM_BUILD_ROOT
 
%files
/usr/sbin/sensu-backend
/etc/sensu
%attr(0755,sensu,sensu) /var/cache/sensu/
%attr(0750,sensu,sensu) /var/cache/sensu/sensu-backend
%attr(0755,sensu,sensu) /var/lib/sensu/
%attr(0750,sensu,sensu) /var/lib/sensu/sensu-backend
%attr(0750,sensu,sensu) /var/log/sensu
%attr(0750,sensu,sensu) /var/run/sensu
/usr/share/doc/sensu-go-backend-%{version}/LICENSE.txt
/usr/share/doc/sensu-go-backend-%{version}/backend.yml.example
/lib/systemd/system/sensu-go-backend.service
 
%post
[ ! -e /etc/sensu/backend.yml ] && cp /usr/share/doc/sensu-go-backend-%{version}/backend.yml.example /etc/sensu/backend.yml
[ ! -e /etc/systemd/system/sensu-go-backend.service ] && ln -s /lib/systemd/system/sensu-go-backend.service /etc/systemd/system/sensu-go-backend.service
 
%preun
[ "$1" == "0" ] && systemctl stop sensu-go-backend.service
[ "$1" == "0" ] && [ -h /etc/systemd/system/sensu-go-backend.service ] && unlink /etc/systemd/system/sensu-go-backend.service 

%postun