diff --git a/.github/workflows/base-ci-goreleaser.yaml b/.github/workflows/base-ci-goreleaser.yaml index 3418d13b..a75c16b9 100644 --- a/.github/workflows/base-ci-goreleaser.yaml +++ b/.github/workflows/base-ci-goreleaser.yaml @@ -74,3 +74,10 @@ jobs: GOOS: ${{ matrix.GOOS }} GOARCH: ${{ matrix.GOARCH }} GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} + + - name: Upload linux service packages + if: ${{ matrix.GOOS == 'linux' && matrix.GOARCH == 'amd64' }} + uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + with: + name: linux-packages + path: distributions/${{ inputs.distribution }}/dist/linux_amd64/* diff --git a/.github/workflows/ci-goreleaser-contrib.yaml b/.github/workflows/ci-goreleaser-contrib.yaml index 9d01d015..ad7db0a6 100644 --- a/.github/workflows/ci-goreleaser-contrib.yaml +++ b/.github/workflows/ci-goreleaser-contrib.yaml @@ -31,3 +31,11 @@ jobs: goos: '[ "linux", "windows", "darwin" ]' goarch: '[ "386", "amd64", "arm64", "ppc64le", "arm", "s390x" ]' secrets: inherit + + package-tests: + name: Package tests + needs: check-goreleaser + uses: ./.github/workflows/package-tests.yaml + with: + distribution: otelcol-contrib + type: '[ "deb", "rpm" ]' diff --git a/.github/workflows/ci-goreleaser-core.yaml b/.github/workflows/ci-goreleaser-core.yaml index 5b447676..50410eb7 100644 --- a/.github/workflows/ci-goreleaser-core.yaml +++ b/.github/workflows/ci-goreleaser-core.yaml @@ -32,3 +32,11 @@ jobs: goos: '[ "linux", "windows", "darwin" ]' goarch: '[ "386", "amd64", "arm64", "ppc64le", "arm", "s390x" ]' secrets: inherit + + package-tests: + name: Package tests + needs: check-goreleaser + uses: ./.github/workflows/package-tests.yaml + with: + distribution: otelcol + type: '[ "deb", "rpm" ]' diff --git a/.github/workflows/package-tests.yaml b/.github/workflows/package-tests.yaml new file mode 100644 index 00000000..90429064 --- /dev/null +++ b/.github/workflows/package-tests.yaml @@ -0,0 +1,30 @@ +name: Package Tests + +on: + workflow_call: + inputs: + type: + required: true + type: string + distribution: + required: true + type: string + +jobs: + package-tests: + name: Package Tests + runs-on: ubuntu-latest + strategy: + matrix: + type: ${{ fromJSON(inputs.type) }} + steps: + - name: Checkout + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Download built artifacts + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + name: linux-packages + + - name: Test ${{ matrix.type }} package + run: ./scripts/package-tests/package-tests.sh ./otelcol*-SNAPSHOT-*_linux_amd64.${{ matrix.type }} ${{ inputs.distribution }} diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 845cb6d7..65794f52 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -13,3 +13,5 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Run ShellCheck uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0 + env: + SHELLCHECK_OPTS: -x diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 0d0c3d3b..8a776314 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,3 +1,4 @@ +version: 2 release: header: | Check the [v{{.Version}} contrib changelog](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tags/v{{.Version}}) and the [v{{.Version}} core changelog](https://github.com/open-telemetry/opentelemetry-collector/releases/tags/v{{.Version}}) for changelogs on specific components. diff --git a/distributions/otelcol-k8s/.goreleaser.yaml b/distributions/otelcol-k8s/.goreleaser.yaml index f7d91cc2..8e6aa0f0 100644 --- a/distributions/otelcol-k8s/.goreleaser.yaml +++ b/distributions/otelcol-k8s/.goreleaser.yaml @@ -1,5 +1,6 @@ partial: by: target +version: 2 project_name: opentelemetry-collector-releases env: - COSIGN_YES=true diff --git a/go.mod b/go.mod index 93f95434..0cb7003d 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,6 @@ require ( require ( github.com/kr/pretty v0.3.1 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/go.sum b/go.sum index 0ced732f..0ff890e8 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/scripts/package-tests/Dockerfile.test.deb b/scripts/package-tests/Dockerfile.test.deb new file mode 100644 index 00000000..fd17c383 --- /dev/null +++ b/scripts/package-tests/Dockerfile.test.deb @@ -0,0 +1,14 @@ +# A debian image with systemd enabled. Must be run with: +# `-d --privileged -v /sys/fs/cgroup:/sys/fs/cgroup:ro` flags +FROM debian:12 + +ENV container docker +ENV DEBIAN_FRONTEND noninteractive + +# Enable systemd. +RUN apt-get update ; \ + apt-get install -y systemd systemd-sysv procps; \ + apt-get clean ; \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +CMD ["/lib/systemd/systemd"] diff --git a/scripts/package-tests/Dockerfile.test.rpm b/scripts/package-tests/Dockerfile.test.rpm new file mode 100644 index 00000000..9e7cf481 --- /dev/null +++ b/scripts/package-tests/Dockerfile.test.rpm @@ -0,0 +1,9 @@ +# A rockylinux image with systemd enabled. Must be run with: +# `-d --privileged -v /sys/fs/cgroup:/sys/fs/cgroup:ro` flags +FROM rockylinux:9.3 + +ENV container docker + +RUN dnf install -y initscripts + +CMD ["/usr/sbin/init"] diff --git a/scripts/package-tests/README.md b/scripts/package-tests/README.md new file mode 100644 index 00000000..924565ac --- /dev/null +++ b/scripts/package-tests/README.md @@ -0,0 +1,26 @@ +# Build and test deb/rpm/apk packages + +## Prerequisites + +Tools: + +- [Go](https://go.dev/) +- [GoReleaser](https://goreleaser.com/) +- [Podman](https://podman.io/) +- make + +## How to build and test + +To build the Collector Linux packages, a few steps are required: + +- Run `make generate` to (re-)generate sources and GoReleaser files +- Go to the distribution folder that you want to build (under the `distributions` folder) +- Run `goreleaser release --snapshot --clean --skip sbom,sign,archive,docker` + - This will build the necessary release assets with all architectures and packaging types into the `dist` folder inside your + current folder. (We can skip many parts of the release build that we don't need for running the package tests locally) + - We use GoReleaser Pro only features in CI. If you want to run this locally, and you run into `unmarshal` errors, + you may have to remove the parts that goreleaser complains about or use a pro license. +- Go back to the root of the repo +- To start the package tests, + run: `./scripts/package-tests/package-tests.sh ./distributions//dist/_*-SNAPSHOT-*_linux_amd64. ` + diff --git a/scripts/package-tests/common.sh b/scripts/package-tests/common.sh new file mode 100755 index 00000000..18c859e2 --- /dev/null +++ b/scripts/package-tests/common.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +podman_cp() { + local container="$1" + local src="$2" + local dest="$3" + local dest_dir + dest_dir="$( dirname "$dest" )" + + echo "Copying $src to $container:$dest ..." + podman exec "$container" mkdir -p "$dest_dir" + podman cp "$src" "$container":"$dest" +} + +install_pkg() { + local container="$1" + local pkg_path="$2" + local pkg_base + pkg_base=$( basename "$pkg_path" ) + + echo "Installing $pkg_base ..." + podman_cp "$container" "$pkg_path" /tmp/"$pkg_base" + if [[ "${pkg_base##*.}" = "deb" ]]; then + podman exec "$container" dpkg -i /tmp/"$pkg_base" + else + podman exec "$container" rpm -ivh /tmp/"$pkg_base" + fi +} + +uninstall_pkg() { + local container="$1" + local pkg_type="$2" + local pkg_name="$3" + + echo "Uninstalling $pkg_name ..." + if [[ "$pkg_type" = "deb" ]]; then + podman exec "$container" dpkg -r "$pkg_name" + else + podman exec "$container" rpm -e "$pkg_name" + fi +} diff --git a/scripts/package-tests/package-tests.sh b/scripts/package-tests/package-tests.sh new file mode 100755 index 00000000..543f626f --- /dev/null +++ b/scripts/package-tests/package-tests.sh @@ -0,0 +1,99 @@ +#!/bin/bash + +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +set -euov pipefail + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +REPO_DIR="$( cd "$SCRIPT_DIR/../../../../" && pwd )" +export REPO_DIR +PKG_PATH="${1:-}" +DISTRO="${2:-}" + +SERVICE_NAME=$DISTRO +PROCESS_NAME=$DISTRO + +# shellcheck source=scripts/package-tests/common.sh +source "$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_base="$( basename "$PKG_PATH" )" +pkg_type="${pkg_base##*.}" +if [[ ! "$pkg_type" =~ ^(deb|rpm)$ ]]; then + echo "$PKG_PATH not supported!" >&2 + exit 1 +fi +image_name="otelcontribcol-$pkg_type-test" +container_name="$image_name" +container_exec="podman exec $container_name" + +trap 'podman rm -fv $container_name >/dev/null 2>&1 || true' EXIT + +podman build -t "$image_name" -f "$SCRIPT_DIR/Dockerfile.test.$pkg_type" "$SCRIPT_DIR" +podman rm -fv "$container_name" >/dev/null 2>&1 || true + +# test install +CRUN_VER='1.14.4' +mkdir -p "${HOME}/.local/bin" +curl -L "https://github.com/containers/crun/releases/download/${CRUN_VER}/crun-${CRUN_VER}-linux-amd64" -o "${HOME}/.local/bin/crun" +chmod +x "${HOME}/.local/bin/crun" +mkdir -p "${HOME}/.config/containers" +cat << EOF > "${HOME}/.config/containers/containers.conf" +[engine.runtimes] +crun = [ + "${HOME}/.local/bin/crun", + "/usr/bin/crun" +] +EOF + +echo +podman run --name "$container_name" -d "$image_name" +install_pkg "$container_name" "$PKG_PATH" + +# ensure service has started and still running after 5 seconds +sleep 5 +echo "Checking $SERVICE_NAME service status ..." +$container_exec systemctl --no-pager status "$SERVICE_NAME" + +echo "Checking $PROCESS_NAME process ..." +if [ "$DISTRO" = "otelcol" ]; then + $container_exec pgrep -a -u otel "$PROCESS_NAME" +else + $container_exec pgrep -a -u otelcol-contrib "$PROCESS_NAME" +fi + +# test uninstall +echo +uninstall_pkg "$container_name" "$pkg_type" "$DISTRO" + +echo "Checking $SERVICE_NAME service status after uninstall ..." +if $container_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 $container_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 $container_exec pgrep "$PROCESS_NAME"; then + echo "$PROCESS_NAME process still running after uninstall" + exit 1 +fi +echo "$PROCESS_NAME process successfully killed after uninstall"