From e2876acb6ff03aaa06a6a370e2fef394f35b2650 Mon Sep 17 00:00:00 2001 From: Jeff Cheng Date: Mon, 13 Jul 2020 11:44:09 -0400 Subject: [PATCH] Add basic systemd service to deb and rpm - Install `otel-collector` systemd service and run as `otel:otel` user:group - Add simple installation test scripts using systemd-in-docker images --- .circleci/config.yml | 14 ++-- internal/buildscripts/packaging/fpm/common.sh | 45 ++++++++++++ .../packaging/fpm/deb/Dockerfile.test | 22 ++++++ .../buildscripts/packaging/fpm/deb/README.md | 5 +- .../buildscripts/packaging/fpm/deb/build.sh | 24 ++++-- .../packaging/fpm/otel-collector.service | 14 ++++ .../buildscripts/packaging/fpm/postinstall.sh | 8 ++ .../buildscripts/packaging/fpm/preinstall.sh | 3 + .../packaging/fpm/preuninstall.sh | 6 ++ .../packaging/fpm/rpm/Dockerfile.test | 20 +++++ .../buildscripts/packaging/fpm/rpm/README.md | 5 +- .../buildscripts/packaging/fpm/rpm/build.sh | 26 ++++--- internal/buildscripts/packaging/fpm/test.sh | 73 +++++++++++++++++++ 13 files changed, 239 insertions(+), 26 deletions(-) create mode 100755 internal/buildscripts/packaging/fpm/common.sh create mode 100644 internal/buildscripts/packaging/fpm/deb/Dockerfile.test create mode 100644 internal/buildscripts/packaging/fpm/otel-collector.service create mode 100755 internal/buildscripts/packaging/fpm/postinstall.sh create mode 100755 internal/buildscripts/packaging/fpm/preinstall.sh create mode 100755 internal/buildscripts/packaging/fpm/preuninstall.sh create mode 100644 internal/buildscripts/packaging/fpm/rpm/Dockerfile.test create mode 100755 internal/buildscripts/packaging/fpm/test.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 4589ece57e2..07aff556ed9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -348,7 +348,8 @@ jobs: paths: project/bin build-package: - executor: golang + machine: + image: ubuntu-1604:202007-01 parameters: package_type: type: enum @@ -361,7 +362,8 @@ jobs: command: | sudo apt-get update sudo apt-get install -y ruby ruby-dev rubygems build-essential rpm - sudo gem install --no-document fpm -v 1.11.0 + gem install --no-document --user-install fpm -v 1.11.0 + echo "PATH=$HOME/.gem/ruby/2.3.0/bin:$PATH" >> $BASH_ENV - run: name: Build << parameters.package_type >> amd64 package command: ./internal/buildscripts/packaging/fpm/<< parameters.package_type >>/build.sh "${CIRCLE_TAG:-}" "amd64" "./bin/" @@ -369,15 +371,13 @@ jobs: name: Build << parameters.package_type >> arm64 package command: ./internal/buildscripts/packaging/fpm/<< parameters.package_type >>/build.sh "${CIRCLE_TAG:-}" "arm64" "./bin/" - run: - name: Test << parameters.package_type >> package installation + name: Test << parameters.package_type >> package command: | if [[ "<< parameters.package_type >>" = "deb" ]]; then - sudo dpkg -i bin/otel-collector*amd64.deb + ./internal/buildscripts/packaging/fpm/test.sh bin/otel-collector*amd64.deb else - sudo rpm -ivh bin/otel-collector*x86_64.rpm + ./internal/buildscripts/packaging/fpm/test.sh bin/otel-collector*x86_64.rpm fi - test -f /usr/bin/otelcol || (echo "/usr/bin/otelcol not found!" && exit 1) - test -f /etc/otel-collector/config.yaml || (echo "/etc/otel-collector/config.yaml not found" && exit 1) - persist_to_workspace: root: ~/ paths: project/bin \ No newline at end of file diff --git a/internal/buildscripts/packaging/fpm/common.sh b/internal/buildscripts/packaging/fpm/common.sh new file mode 100755 index 00000000000..dade87447ab --- /dev/null +++ b/internal/buildscripts/packaging/fpm/common.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +FPM_DIR="$( cd "$( dirname ${BASH_SOURCE[0]} )" && pwd )" + +PKG_NAME="otel-collector" +PKG_VENDOR="OpenTelemetry Community" +PKG_MAINTAINER="OpenTelemetry Community " +PKG_DESCRIPTION="OpenTelemetry Collector" +PKG_LICENSE="Apache 2.0" +PKG_URL="https://github.com/open-telemetry/opentelemetry-collector" +PKG_USER="otel" +PKG_GROUP="otel" + +SERVICE_NAME="otel-collector" +PROCESS_NAME="otelcol" + +SERVICE_PATH="$FPM_DIR/$SERVICE_NAME.service" +PREINSTALL_PATH="$FPM_DIR/preinstall.sh" +POSTINSTALL_PATH="$FPM_DIR/postinstall.sh" +PREUNINSTALL_PATH="$FPM_DIR/preuninstall.sh" + +install_pkg() { + local pkg_path="$1" + local pkg_base=$( basename "$pkg_path" ) + + echo "Installing $pkg_base ..." + docker cp "$pkg_path" $image_name:/tmp/$pkg_base + if [[ "${pkg_base##*.}" = "deb" ]]; then + $docker_exec dpkg -i /tmp/$pkg_base + else + $docker_exec rpm -ivh /tmp/$pkg_base + fi +} + +uninstall_pkg() { + local pkg_type="$1" + local pkg_name="${2:-"$PKG_NAME"}" + + echo "Uninstalling $pkg_name ..." + if [[ "$pkg_type" = "deb" ]]; then + $docker_exec dpkg -r $pkg_name + else + $docker_exec rpm -e $pkg_name + fi +} \ No newline at end of file diff --git a/internal/buildscripts/packaging/fpm/deb/Dockerfile.test b/internal/buildscripts/packaging/fpm/deb/Dockerfile.test new file mode 100644 index 00000000000..f4a7ce84aeb --- /dev/null +++ b/internal/buildscripts/packaging/fpm/deb/Dockerfile.test @@ -0,0 +1,22 @@ +# A debian9 image with systemd enabled. Must be run with: +# `-d --privileged -v /sys/fs/cgroup:/sys/fs/cgroup:ro` flags +FROM debian:9 + +RUN apt-get update &&\ + apt-get install -yq ca-certificates procps systemd + +ENV container docker +RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i = \ + "systemd-tmpfiles-setup.service" ] || rm -f $i; done); \ + rm -f /lib/systemd/system/multi-user.target.wants/*;\ + rm -f /lib/systemd/system/local-fs.target.wants/*; \ + rm -f /lib/systemd/system/sockets.target.wants/*udev*; \ + rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \ + rm -f /lib/systemd/system/anaconda.target.wants/*; + +RUN systemctl set-default multi-user.target +ENV init /lib/systemd/systemd + +VOLUME [ "/sys/fs/cgroup" ] + +ENTRYPOINT ["/lib/systemd/systemd"] diff --git a/internal/buildscripts/packaging/fpm/deb/README.md b/internal/buildscripts/packaging/fpm/deb/README.md index db9eca1c2ff..5fd5ca74017 100644 --- a/internal/buildscripts/packaging/fpm/deb/README.md +++ b/internal/buildscripts/packaging/fpm/deb/README.md @@ -7,4 +7,7 @@ To build the deb package, run `make deb-package` from the repo root directory. T By default, `` is `amd64` and `` is the latest git tag with `-post` appended, e.g. `1.2.3-post`. To override these defaults, set the `ARCH` and `VERSION` environment variables, e.g. -`ARCH=arm64 VERSION=4.5.6 make deb-package`. \ No newline at end of file +`ARCH=arm64 VERSION=4.5.6 make deb-package`. + +Run `./internal/buildscripts/packaging/fpm/test.sh PATH_TO_DEB_FILE` to run a basic installation test with the built +package. \ No newline at end of file diff --git a/internal/buildscripts/packaging/fpm/deb/build.sh b/internal/buildscripts/packaging/fpm/deb/build.sh index 5deae33310d..b21b945cf4e 100755 --- a/internal/buildscripts/packaging/fpm/deb/build.sh +++ b/internal/buildscripts/packaging/fpm/deb/build.sh @@ -7,22 +7,30 @@ REPO_DIR="$( cd "$SCRIPT_DIR/../../../../../" && pwd )" VERSION="${1:-}" ARCH="${2:-"amd64"}" OUTPUT_DIR="${3:-"$REPO_DIR/bin/"}" -OTELCOL_PATH="$REPO_DIR/bin/otelcol_linux_amd64" +OTELCOL_PATH="$REPO_DIR/bin/otelcol_linux_$ARCH" CONFIG_PATH="$REPO_DIR/examples/otel-local-config.yaml" +. $SCRIPT_DIR/../common.sh + if [[ -z "$VERSION" ]]; then latest_tag="$( git describe --abbrev=0 --match v[0-9]* )" VERSION="${latest_tag}-post" fi -fpm -s dir -t deb -n otel-collector -v ${VERSION#v} -f -p "$OUTPUT_DIR" \ - --vendor "OpenTelemetry Community" \ - --maintainer "OpenTelemetry Community " \ - --description "OpenTelemetry Collector" \ - --license "Apache 2.0" \ - --url "https://github.com/open-telemetry/opentelemetry-collector" \ +fpm -s dir -t deb -n $PKG_NAME -v ${VERSION#v} -f -p "$OUTPUT_DIR" \ + --vendor "$PKG_VENDOR" \ + --maintainer "$PKG_MAINTAINER" \ + --description "$PKG_DESCRIPTION" \ + --license "$PKG_LICENSE" \ + --url "$PKG_URL" \ --architecture "$ARCH" \ --config-files /etc/otel-collector/config.yaml \ --deb-dist "stable" \ - $OTELCOL_PATH=/usr/bin/otelcol \ + --deb-user "$PKG_USER" \ + --deb-group "$PKG_GROUP" \ + --before-install "$PREINSTALL_PATH" \ + --after-install "$POSTINSTALL_PATH" \ + --pre-uninstall "$PREUNINSTALL_PATH" \ + $OTELCOL_PATH=/usr/bin/$PROCESS_NAME \ + $SERVICE_PATH=/lib/systemd/system/$SERVICE_NAME.service \ $CONFIG_PATH=/etc/otel-collector/config.yaml \ No newline at end of file diff --git a/internal/buildscripts/packaging/fpm/otel-collector.service b/internal/buildscripts/packaging/fpm/otel-collector.service new file mode 100644 index 00000000000..faca2be7ce1 --- /dev/null +++ b/internal/buildscripts/packaging/fpm/otel-collector.service @@ -0,0 +1,14 @@ +[Unit] +Description=OpenTelemety Collector +After=network.target + +[Service] +ExecStart=/usr/bin/otelcol --config /etc/otel-collector/config.yaml +KillMode=mixed +Restart=on-failure +Type=simple +User=otel +Group=otel + +[Install] +WantedBy=multi-user.target diff --git a/internal/buildscripts/packaging/fpm/postinstall.sh b/internal/buildscripts/packaging/fpm/postinstall.sh new file mode 100755 index 00000000000..42f65782cc2 --- /dev/null +++ b/internal/buildscripts/packaging/fpm/postinstall.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +if command -v systemctl >/dev/null 2>&1; then + systemctl enable otel-collector.service + if [ -f /etc/otel-collector/config.yaml ]; then + systemctl start otel-collector.service + fi +fi \ No newline at end of file diff --git a/internal/buildscripts/packaging/fpm/preinstall.sh b/internal/buildscripts/packaging/fpm/preinstall.sh new file mode 100755 index 00000000000..3a22ca45b55 --- /dev/null +++ b/internal/buildscripts/packaging/fpm/preinstall.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +getent passwd otel >/dev/null || useradd --system --user-group --no-create-home --shell /sbin/nologin otel \ No newline at end of file diff --git a/internal/buildscripts/packaging/fpm/preuninstall.sh b/internal/buildscripts/packaging/fpm/preuninstall.sh new file mode 100755 index 00000000000..13408244a13 --- /dev/null +++ b/internal/buildscripts/packaging/fpm/preuninstall.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +if command -v systemctl >/dev/null 2>&1; then + systemctl stop otel-collector.service + systemctl disable otel-collector.service +fi \ No newline at end of file diff --git a/internal/buildscripts/packaging/fpm/rpm/Dockerfile.test b/internal/buildscripts/packaging/fpm/rpm/Dockerfile.test new file mode 100644 index 00000000000..737aea6459a --- /dev/null +++ b/internal/buildscripts/packaging/fpm/rpm/Dockerfile.test @@ -0,0 +1,20 @@ +# A centos8 image with systemd enabled. Must be run with: +# `-d --privileged -v /sys/fs/cgroup:/sys/fs/cgroup:ro` flags +FROM centos:8 + +ENV container docker + +RUN dnf install -y initscripts + +RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i = \ + "systemd-tmpfiles-setup.service" ] || rm -f $i; done); \ + rm -f /lib/systemd/system/multi-user.target.wants/*;\ + rm -f /lib/systemd/system/local-fs.target.wants/*; \ + rm -f /lib/systemd/system/sockets.target.wants/*udev*; \ + rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \ + rm -f /lib/systemd/system/basic.target.wants/*;\ + rm -f /lib/systemd/system/anaconda.target.wants/*; + +VOLUME [ "/sys/fs/cgroup" ] + +CMD ["/usr/sbin/init"] diff --git a/internal/buildscripts/packaging/fpm/rpm/README.md b/internal/buildscripts/packaging/fpm/rpm/README.md index b31e42eb147..1a99458c75e 100644 --- a/internal/buildscripts/packaging/fpm/rpm/README.md +++ b/internal/buildscripts/packaging/fpm/rpm/README.md @@ -7,4 +7,7 @@ To build the rpm package, run `make rpm-package` from the repo root directory. T By default, `` is `amd64` and `` is the latest git tag with `~post` appended, e.g. `1.2.3~post`. To override these defaults, set the `ARCH` and `VERSION` environment variables, e.g. -`ARCH=arm64 VERSION=4.5.6 make rpm-package`. \ No newline at end of file +`ARCH=arm64 VERSION=4.5.6 make rpm-package`. + +Run `./internal/buildscripts/packaging/fpm/test.sh PATH_TO_RPM_FILE` to run a basic installation test with the built +package. \ No newline at end of file diff --git a/internal/buildscripts/packaging/fpm/rpm/build.sh b/internal/buildscripts/packaging/fpm/rpm/build.sh index 121d18097b5..b7d4d7385f6 100755 --- a/internal/buildscripts/packaging/fpm/rpm/build.sh +++ b/internal/buildscripts/packaging/fpm/rpm/build.sh @@ -7,22 +7,30 @@ REPO_DIR="$( cd "$SCRIPT_DIR/../../../../../" && pwd )" VERSION="${1:-}" ARCH="${2:-"amd64"}" OUTPUT_DIR="${3:-"$REPO_DIR/bin/"}" -OTELCOL_PATH="$REPO_DIR/bin/otelcol_linux_amd64" +OTELCOL_PATH="$REPO_DIR/bin/otelcol_linux_$ARCH" CONFIG_PATH="$REPO_DIR/examples/otel-local-config.yaml" +. $SCRIPT_DIR/../common.sh + if [[ -z "$VERSION" ]]; then latest_tag="$( git describe --abbrev=0 --match v[0-9]* )" VERSION="${latest_tag}~post" fi -fpm -s dir -t rpm -n otel-collector -v ${VERSION#v} -f -p "$OUTPUT_DIR" \ - --vendor "OpenTelemetry Community" \ - --maintainer "OpenTelemetry Community " \ - --description "OpenTelemetry Collector" \ - --license "Apache 2.0" \ - --rpm-summary "OpenTelemetry Collector" \ - --url "https://github.com/open-telemetry/opentelemetry-collector" \ +fpm -s dir -t rpm -n $PKG_NAME -v ${VERSION#v} -f -p "$OUTPUT_DIR" \ + --vendor "$PKG_VENDOR" \ + --maintainer "$PKG_MAINTAINER" \ + --description "$PKG_DESCRIPTION" \ + --license "$PKG_LICENSE" \ + --url "$PKG_URL" \ --architecture "$ARCH" \ --config-files /etc/otel-collector/config.yaml \ - $OTELCOL_PATH=/usr/bin/otelcol \ + --rpm-summary "$PKG_DESCRIPTION" \ + --rpm-user "$PKG_USER" \ + --rpm-group "$PKG_GROUP" \ + --before-install "$PREINSTALL_PATH" \ + --after-install "$POSTINSTALL_PATH" \ + --pre-uninstall "$PREUNINSTALL_PATH" \ + $OTELCOL_PATH=/usr/bin/$PROCESS_NAME \ + $SERVICE_PATH=/lib/systemd/system/$SERVICE_NAME.service \ $CONFIG_PATH=/etc/otel-collector/config.yaml \ No newline at end of file diff --git a/internal/buildscripts/packaging/fpm/test.sh b/internal/buildscripts/packaging/fpm/test.sh new file mode 100755 index 00000000000..e15d1f270b7 --- /dev/null +++ b/internal/buildscripts/packaging/fpm/test.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +set -euo pipefail + +SCRIPT_DIR="$( cd "$( dirname ${BASH_SOURCE[0]} )" && pwd )" +REPO_DIR="$( cd "$SCRIPT_DIR/../../../../" && pwd )" +PKG_PATH=${1:-} + +. $SCRIPT_DIR/common.sh + +if [[ -z "$PKG_PATH" ]]; then + echo "usage: ${BASH_SOURCE[0]} DEB_OR_RPM_PATH" >&2 + exit 1 +fi + +if [[ ! -f "$PKG_PATH" ]]; then + echo "$PKG_PATH not found!" >&2 + exit 1 +fi + +pkg_name="$( basename "$PKG_PATH" )" +pkg_type="${pkg_name##*.}" +if [[ ! "$pkg_type" =~ ^(deb|rpm)$ ]]; then + echo "$PKG_PATH not supported!" >&2 + exit 1 +fi +image_name="otelcol-$pkg_type-test" +container_name="$image_name" +docker_run="docker run --name $container_name -d -v /sys/fs/cgroup:/sys/fs/cgroup:ro --privileged $image_name" +docker_exec="docker exec $container_name" + +trap "docker rm -fv $container_name >/dev/null 2>&1 || true" EXIT + +docker build -t $image_name -f "$SCRIPT_DIR/$pkg_type/Dockerfile.test" "$SCRIPT_DIR" +docker rm -fv $container_name >/dev/null 2>&1 || true + +# test install +echo +$docker_run +install_pkg "$PKG_PATH" + +# ensure service has started and still running after 5 seconds +sleep 5 +echo "Checking $SERVICE_NAME service status ..." +$docker_exec systemctl --no-pager status $SERVICE_NAME + +echo "Checking $PROCESS_NAME process ..." +$docker_exec pgrep -a -u otel $PROCESS_NAME + +# test uninstall +echo +uninstall_pkg $pkg_type + +echo "Checking $SERVICE_NAME service status after uninstall ..." +if $docker_exec systemctl --no-pager status $SERVICE_NAME; then + echo "$SERVICE_NAME service still running after uninstall!" >&2 + exit 1 +fi +echo "$SERVICE_NAME service successfully stopped after uninstall" + +echo "Checking $SERVICE_NAME service existence after uninstall ..." +if $docker_exec systemctl list-unit-files --all | grep $SERVICE_NAME; then + echo "$SERVICE_NAME service still exists after uninstall!" >&2 + exit 1 +fi +echo "$SERVICE_NAME service successfully removed after uninstall" + +echo "Checking $PROCESS_NAME process after uninstall ..." +if $docker_exec pgrep $PROCESS_NAME; then + echo "$PROCESS_NAME process still running after uninstall!" >&2 + exit 1 +fi +echo "$PROCESS_NAME process successfully killed after uninstall" \ No newline at end of file