Skip to content

Commit

Permalink
ci(injector,instrumentation): run injector&instrumentation tests on CI
Browse files Browse the repository at this point in the history
Also: Enable both tests to use an existing (local or remote)
instrumentation image instead of building the instrumentation image from
local sources.
  • Loading branch information
basti1302 committed Oct 26, 2024
1 parent dba0087 commit 9dd8b6b
Show file tree
Hide file tree
Showing 11 changed files with 270 additions and 68 deletions.
13 changes: 0 additions & 13 deletions .github/actions/build-image/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@ inputs:
githubToken:
description: "github token"
required: true
# dockerhubUsername:
# description: "Dockerhub username"
# required: true
# dockerhubToken:
# description: "Dockerhub token"
# required: true
imageName:
description: "name of the container image (without registry prefix)"
required: true
Expand Down Expand Up @@ -38,12 +32,6 @@ runs:
- name: set up docker buildx
uses: docker/setup-buildx-action@v3

# - name: dockerhub login
# uses: docker/login-action@v3
# with:
# username: ${{ inputs.dockerhubUsername }}
# password: ${{ inputs.dockerhubToken }}

- name: login to GitHub container registry
uses: docker/login-action@v3
with:
Expand All @@ -55,7 +43,6 @@ runs:
id: meta
uses: docker/metadata-action@v5
with:
# add dash0hq/${{ inputs.imageName }} to images once Dockerhub has been set up
images: |
ghcr.io/dash0hq/${{ inputs.imageName }}
tags: |
Expand Down
135 changes: 118 additions & 17 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,19 @@ on:
- '*.md'
workflow_dispatch:

concurrency:
group: ci-concurrency-group-${{ github.ref }}
cancel-in-progress: true

env:
instrumentation_image_test_tag: ghcr.io/dash0hq/instrumentation-ci-test:${{ github.head_ref || github.ref_name }}

jobs:
verify:
name: Build & Test
runs-on: ubuntu-latest
timeout-minutes: 15

concurrency:
group: ci-concurrency-group-${{ github.ref }}
cancel-in-progress: true

steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -59,6 +62,116 @@ jobs:
run: |
make test
injector_binary_and_instrumentation_image_tests:
name: Injector Binary & Instrumentation Image Tests
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4

- name: get branch name
id: branch-name
uses: tj-actions/branch-names@v8

- name: find SHA of last successful workflow run on main branch
uses: nrwl/nx-set-shas@v4
id: last_succsesfull_commit_main_branch
with:
# Get the last successful commit on main branch (actually, on the target branch for the PR, but that is
# usually main).
main-branch-name: ${{ steps.branch-name.outputs.base_ref_branch }}
workflow_id: 'ci.yml'

# We use the changed-files action to potentially skip the injector & instrumentation tests on PRs that contain no
# changes for the instrumentation image. This is because running the tests requires to build the instrumentation
# image for both arm64 and X86_64, and the cross-platform build is very slow (takes up to 15 minutes). We do
# always run these steps when building the main branch or a tag though. By default, changed-files would compare
# against the last non-merge commit on the target branch for pull request events (which is used in PR builds), but
# together with the nrwl/nx-set-shas step from above we compare against the SHA from the last _successful_ CI
# workflow run on the main branch.
- name: compile list of relevant changed files for the instrumentation image
id: changed-files
uses: tj-actions/changed-files@v44
with:
base_sha: ${{ steps.last_succsesfull_commit_main_branch.outputs.base }}
files_yaml: |
instrumentation:
- .github/workflows/ci.yaml
- images/instrumentation/**
- name: show changed files
env:
INSTRUMENTATION_CHANGED_FILES_FLAG: ${{ steps.changed-files.outputs.instrumentation_any_changed }}
INSTRUMENTATION_CHANGED_FILES_LIST: ${{ steps.changed-files.outputs.instrumentation_all_changed_files }}
run: |
echo "files for instrumentation image have changed: $INSTRUMENTATION_CHANGED_FILES_FLAG"
echo "changed files for instrumentation image: $INSTRUMENTATION_CHANGED_FILES_LIST"
- name: set up docker buildx
uses: docker/setup-buildx-action@v3
if: |
steps.changed-files.outputs.instrumentation_any_changed == 'true' ||
github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/')
# Just for building on arm, buildx is enough but doing docker run with --platform=linux/arm64 (which we do when
# testing the injector binary and the instrumentation image) requires qemu.
- name: set up qemu
uses: docker/setup-qemu-action@v3
if: |
steps.changed-files.outputs.instrumentation_any_changed == 'true' ||
github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/')
- name: login to GitHub container registry
uses: docker/login-action@v3
if: |
steps.changed-files.outputs.instrumentation_any_changed == 'true' ||
github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/')
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: build temporary instrumentation image
uses: docker/build-push-action@v6
if: |
steps.changed-files.outputs.instrumentation_any_changed == 'true' ||
github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/')
with:
context: images/instrumentation
tags: ${{ env.instrumentation_image_test_tag }}
platforms: linux/amd64,linux/arm64
cache-from: type=gha,scope=instrumentation
cache-to: type=gha,mode=max,scope=instrumentation
push: true

- name: injector tests
if: |
steps.changed-files.outputs.instrumentation_any_changed == 'true' ||
github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/')
env:
INSTRUMENTATION_IMAGE: ${{ env.instrumentation_image_test_tag }}
run: |
images/instrumentation/injector/test/scripts/test-all.sh
- name: instrumentation image tests
if: |
steps.changed-files.outputs.instrumentation_any_changed == 'true' ||
github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/')
env:
INSTRUMENTATION_IMAGE: ${{ env.instrumentation_image_test_tag }}
run: |
images/instrumentation/test/test-all.sh
- name: delete test image
uses: bots-house/[email protected]
if: ${{ always() && ( steps.changed-files.outputs.instrumentation_any_changed == 'true' || github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/') ) }}
with:
owner: dash0hq
name: instrumentation-ci-test
token: ${{ secrets.GITHUB_TOKEN }}
# delete untagged images from this build (and from earlier builds, if there are any leftovers)
untagged-keep-latest: 1

# Builds and potentially pushes all container images. For pushes to PRs/branches, we simply verify that the image
# build still works, the resulting image will not be pushed to any target registry. For pushes to the main branch, the
# images are tagged with "main-dev", but not with a version x.y.z. Finally, for pushes to a tag (or when a tag is
Expand All @@ -70,9 +183,7 @@ jobs:
runs-on: ubuntu-latest
needs:
- verify
concurrency:
group: ci-concurrency-group-${{ github.ref }}
cancel-in-progress: true
- injector_binary_and_instrumentation_image_tests
timeout-minutes: 60

steps:
Expand Down Expand Up @@ -138,10 +249,6 @@ jobs:
if: ${{ ! contains(github.ref, 'refs/tags/') && github.actor != 'dependabot[bot]'}}
needs:
- build-and-push-images
concurrency:
group: ci-concurrency-group-${{ github.ref }}
cancel-in-progress: true

steps:
- uses: actions/checkout@v4

Expand All @@ -164,9 +271,6 @@ jobs:
if: ${{ ! contains(github.ref, 'refs/tags/') && github.actor == 'dependabot[bot]'}}
needs:
- build-and-push-images
concurrency:
group: ci-concurrency-group-${{ github.ref }}
cancel-in-progress: true

steps:
- name: skipping publish helm chart (dry run)
Expand All @@ -179,9 +283,6 @@ jobs:
if: ${{ contains(github.ref, 'refs/tags/') && github.actor != 'dependabot[bot]'}}
needs:
- build-and-push-images
concurrency:
group: ci-concurrency-group-${{ github.ref }}
cancel-in-progress: true

steps:
- uses: actions/checkout@v4
Expand Down
8 changes: 4 additions & 4 deletions images/instrumentation/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ RUN apt-get update && \
apt-get autoremove -y && \
apt-get clean -y

COPY ./injector /dash0-init-container/injector
WORKDIR /dash0-init-container/injector
COPY ./injector /dash0-init-container
WORKDIR /dash0-init-container
RUN gcc \
-shared \
-nostdlib \
-fPIC \
-Wl,--version-script=src/dash0_injector.exports.map \
src/dash0_injector.c \
-o bin/dash0_injector.so
-o dash0_injector.so

# build Node.js artifacts
FROM node:20.13.1-alpine3.19 AS build-node.js
Expand All @@ -35,7 +35,7 @@ COPY copy-instrumentation.sh /

# copy artifacts (distros, injector binary) from the build stages to the final image
RUN mkdir -p /dash0-init-container/instrumentation
COPY --from=build-injector /dash0-init-container/injector/bin/dash0_injector.so /dash0-init-container/dash0_injector.so
COPY --from=build-injector /dash0-init-container/dash0_injector.so /dash0-init-container/dash0_injector.so
COPY --from=build-node.js /dash0-init-container/instrumentation/node.js /dash0-init-container/instrumentation/node.js

WORKDIR /
Expand Down
4 changes: 2 additions & 2 deletions images/instrumentation/injector/test/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ function main () {
case "non-existing":
echoEnvVar("DOES_NOT_EXIST");
break;
case "term":
echoEnvVar("TERM");
case "existing":
echoEnvVar("TEST_VAR");
break;
case "node_options":
echoEnvVar("NODE_OPTIONS");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ set -eu

cd "$(dirname "${BASH_SOURCE[0]}")"/../../..

# shellcheck source=images/instrumentation/injector/test/scripts/util
source injector/test/scripts/util

if [ -z "${ARCH:-}" ]; then
ARCH=arm64
fi
Expand Down Expand Up @@ -36,9 +39,5 @@ docker build \
-f "$dockerfile_name" \
-t "$image_name"

container_id=$(docker create "$image_name")
docker container cp \
"$container_id":/dash0-init-container/injector/bin/dash0_injector.so \
injector/test/bin/dash0_injector_"$ARCH".so
docker rm -v "$container_id"
copy_injector_binary_from_container_image "$image_name" "$ARCH" "$docker_platform"

Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ docker run \
--platform "$docker_platform" \
--env EXPECTED_CPU_ARCHITECTURE="$expected_cpu_architecture" \
--name "$container_name" \
-it \
"$image_name" \
$docker_run_extra_arguments
{ set +x; } 2> /dev/null
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ run_test_case() {
existing_node_options_value=${4:-}
set +e
if [ "$existing_node_options_value" != "" ]; then
test_output=$(LD_PRELOAD="$injector_binary" NODE_OPTIONS="$existing_node_options_value" node index.js "$command")
test_output=$(LD_PRELOAD="$injector_binary" TEST_VAR=value NODE_OPTIONS="$existing_node_options_value" node index.js "$command")
else
test_output=$(LD_PRELOAD="$injector_binary" node index.js "$command")
test_output=$(LD_PRELOAD="$injector_binary" TEST_VAR=value node index.js "$command")
fi
test_exit_code=$?
set -e
Expand All @@ -70,7 +70,7 @@ exit_code=0
cd app

run_test_case "getenv: returns undefined for non-existing environment variable" non-existing "DOES_NOT_EXIST: -"
run_test_case "getenv: returns environment variable unchanged" term "TERM: xterm"
run_test_case "getenv: returns environment variable unchanged" existing "TEST_VAR: value"
run_test_case "getenv: overrides NODE_OPTIONS if it is not present" node_options "NODE_OPTIONS: --require /__dash0__/instrumentation/node.js/node_modules/@dash0hq/opentelemetry"
run_test_case "getenv: ask for NODE_OPTIONS (unset) twice" node_options_twice "NODE_OPTIONS: --require /__dash0__/instrumentation/node.js/node_modules/@dash0hq/opentelemetry; NODE_OPTIONS: --require /__dash0__/instrumentation/node.js/node_modules/@dash0hq/opentelemetry"
run_test_case "getenv: prepends to NODE_OPTIONS if it is present" node_options "NODE_OPTIONS: --require /__dash0__/instrumentation/node.js/node_modules/@dash0hq/opentelemetry --no-deprecation" "--no-deprecation"
Expand Down
35 changes: 30 additions & 5 deletions images/instrumentation/injector/test/scripts/test-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ dockerfile_injector_build=injector/test/docker/Dockerfile-build

cd "$(dirname "${BASH_SOURCE[0]}")"/../../..

# shellcheck source=images/instrumentation/injector/test/scripts/util
source injector/test/scripts/util

# remove all outdated injector binaries
rm -rf injector/test/bin/*

Expand Down Expand Up @@ -65,15 +68,37 @@ if [[ ! -e "$dockerfile_injector_build" ]]; then
exit 1
fi

# build injector binary for both architectures
ARCH=arm64 injector/test/scripts/build-in-container.sh
ARCH=x86_64 injector/test/scripts/build-in-container.sh
instrumentation_image=${INSTRUMENTATION_IMAGE:-}
if [[ -z "$instrumentation_image" ]]; then
# build injector binary for both architectures
echo ----------------------------------------
echo building the injector binary locally from source
echo ----------------------------------------
ARCH=arm64 injector/test/scripts/build-in-container.sh
ARCH=x86_64 injector/test/scripts/build-in-container.sh
else
if is_remote_image "$instrumentation_image"; then
echo ----------------------------------------
printf "using injector binary from existing remote image:\n$instrumentation_image\n"
echo ----------------------------------------
docker pull --platform linux/arm64 "$instrumentation_image"
copy_injector_binary_from_container_image "$instrumentation_image" arm64 linux/arm64
docker pull --platform linux/amd64 "$instrumentation_image"
copy_injector_binary_from_container_image "$instrumentation_image" x86_64 linux/amd64
else
echo ----------------------------------------
printf "using injector binary from existing local image:\n$instrumentation_image\n"
echo ----------------------------------------
copy_injector_binary_from_container_image "$instrumentation_image" arm64 linux/arm64
copy_injector_binary_from_container_image "$instrumentation_image" x86_64 linux/amd64
fi
fi
echo

run_tests_for_architecture_and_libc_flavor arm64 glibc
run_tests_for_architecture_and_libc_flavor x86_64 glibc
run_tests_for_architecture_and_libc_flavor arm64 musl
run_tests_for_architecture_and_libc_flavor x86_64 musl

printf "$summary\n"
printf "$summary\n\n"
exit $exit_code

40 changes: 40 additions & 0 deletions images/instrumentation/injector/test/scripts/util
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc.
# SPDX-License-Identifier: Apache-2.0

is_remote_image() {
image_name=${1:-}
if [[ -z "$image_name" ]]; then
echo "error: mandatory argument \"image_name\" is missing"
exit 1
fi

if [[ "$image_name" == *"/"* ]]; then
return 0
else
return 1
fi
}

copy_injector_binary_from_container_image() {
image_name=${1:-}
if [[ -z "$image_name" ]]; then
echo "error: mandatory argument \"image_name\" is missing"
exit 1
fi
arch=${2:-}
if [[ -z "$arch" ]]; then
echo "error: mandatory argument \"arch\" is missing"
exit 1
fi
docker_platform=${3:-}
if [[ -z "$docker_platform" ]]; then
echo "error: mandatory argument \"docker_platform\" is missing"
exit 1
fi

container_id=$(docker create --platform "$docker_platform" "$image_name")
docker container cp \
"$container_id":/dash0-init-container/dash0_injector.so \
injector/test/bin/dash0_injector_"$arch".so
docker rm -v "$container_id"
}
Loading

0 comments on commit 9dd8b6b

Please sign in to comment.