diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d8a3af90..f859e933 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -26,6 +26,12 @@ on: type: boolean description: 'Should slow tests be run?' default: true + registry-service: + required: false + type: string + description: 'registry service OCI image URI' + default: "ghcr.io/project-stacker/registry:2" + secrets: codecov_token: required: true @@ -34,7 +40,7 @@ jobs: build: services: registry: - image: ghcr.io/project-stacker/registry:2 + image: ${{ inputs.registry-service }} ports: - 5000:5000 strategy: @@ -88,6 +94,7 @@ jobs: run: | make stacker VERSION_FULL=${{ inputs.build-id }} env: + REGISTRY_SERVICE: ${{ inputs.registry-service }} REGISTRY_URL: localhost:5000 ZOT_HOST: localhost ZOT_PORT: 8080 @@ -98,6 +105,7 @@ jobs: run: | make check VERSION_FULL=${{ inputs.build-id }} PRIVILEGE_LEVEL=${{ matrix.privilege-level }} env: + REGISTRY_SERVICE: ${{ inputs.registry-service }} REGISTRY_URL: localhost:5000 ZOT_HOST: localhost ZOT_PORT: 8080 @@ -113,7 +121,7 @@ jobs: make publish-stacker-bin - name: Upload artifacts uses: actions/upload-artifact@v4 - if: ${{ (matrix.privilege-level == 'priv') && (matrix.go-version == '1.23.x') }} + if: ${{ (matrix.privilege-level == 'priv') }} with: # if there is more than 1 go-version, we would need to account for that here. name: stacker-${{ matrix.platform }} diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index ac6a6ac7..48ac5daf 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -26,6 +26,12 @@ on: type: boolean description: 'Should slow tests be run?' default: true + registry-service: + required: false + type: string + description: 'registry service OCI image URI' + default: "ghcr.io/project-stacker/registry:2" + secrets: codecov_token: required: true @@ -35,7 +41,7 @@ jobs: runs-on: ubuntu-24.04 services: registry: - image: ghcr.io/project-stacker/registry:2 + image: ${{ inputs.registry-service }} ports: - 5000:5000 strategy: @@ -91,6 +97,7 @@ jobs: go tool covdata percent -i $GOCOVERDIR ls -altR $GOCOVERDIR env: + REGISTRY_SERVICE: ${{ inputs.registry-service }} REGISTRY_URL: localhost:5000 ZOT_HOST: localhost ZOT_PORT: 8080 @@ -104,7 +111,7 @@ jobs: files: coverage-${{ matrix.privilege-level}}.txt - name: Upload artifacts uses: actions/upload-artifact@v4 - if: ${{ (matrix.privilege-level == 'priv') && (matrix.go-version == '1.23.x') }} + if: ${{ (matrix.privilege-level == 'priv') }} with: # if there is more than 1 go-version, we would need to account for that here. name: binary-cov diff --git a/install-build-deps.sh b/install-build-deps.sh index 954db821..9df26332 100755 --- a/install-build-deps.sh +++ b/install-build-deps.sh @@ -69,6 +69,10 @@ installdeps_ubuntu() { lxc-utils ) + if ! command -v add-apt-repository; then + sudo apt-get -y install software-properties-common + fi + case "$VERSION_ID" in 22.04) sudo add-apt-repository -y ppa:project-machine/squashfuse diff --git a/test/bom.bats b/test/bom.bats index ed4d065e..ea60cf93 100644 --- a/test/bom.bats +++ b/test/bom.bats @@ -22,6 +22,7 @@ function teardown() { @test "all container contents must be accounted for" { skip_slow_test + skip_broken_tests cat > stacker.yaml <<"EOF" bom-parent: from: diff --git a/test/convert.bats b/test/convert.bats index 18fbe8d8..f173b8a6 100644 --- a/test/convert.bats +++ b/test/convert.bats @@ -1,5 +1,13 @@ load helpers +function setup_file() { + start_registry +} + +function teardown_file() { + stop_registry +} + function setup() { stacker_setup } diff --git a/test/helpers.bash b/test/helpers.bash index d74d53c9..5e08be87 100644 --- a/test/helpers.bash +++ b/test/helpers.bash @@ -135,6 +135,16 @@ function skip_slow_test { esac } +function skip_broken_tests { + case "${BROKEN_TESTS:-false}" in + true) return;; + false) skip "${BATS_TEST_NAME} is broken. Set BROKEN_TESTS=true to run.";; + *) stderr "BROKEN_TESTS variable must be 'true' or 'false'" \ + "found '${BROKEN_TESTS}'" + return 1;; + esac +} + function tmpd() { mktemp -d "${PWD}/stackertest${1:+-$1}.XXXXXX" } @@ -221,10 +231,15 @@ EOF } function write_auth_zot_config { + if ! check_env_zot; then + echo "ERROR: invalid zot env values" + return 1 + fi htpasswd -Bbn iam careful >> $TEST_TMPDIR/htpasswd - cat > $TEST_TMPDIR/zot-config.json << EOF + zotcfg=$TEST_TMPDIR/zot-config.json + cat > "$zotcfg" << EOF { "distSpecVersion": "1.1.0-dev", "storage": { @@ -262,15 +277,18 @@ function write_auth_zot_config { } } EOF - + echo "zot config:" + cat "$zotcfg" } function zot_setup { + zot_teardown write_plain_zot_config start_zot } function zot_setup_auth { + zot_teardown write_auth_zot_config start_zot USE_TLS } @@ -279,12 +297,18 @@ function start_zot { ZOT_USE_TLS=$1 echo "# starting zot at $ZOT_HOST:$ZOT_PORT" >&3 # start as a background task - zot verify $TEST_TMPDIR/zot-config.json - zot serve $TEST_TMPDIR/zot-config.json & + zotcfg="$TEST_TMPDIR/zot-config.json" + if ! zot verify "$zotcfg"; then + echo "zot failed to verify zot config:" + cat "$zotcfg" + return 1 + fi + zot serve "$TEST_TMPDIR/zot-config.json" & pid=$! + echo "$pid" > "${TEST_TMPDIR}/zot.pid" echo "zot is running at pid $pid" - cat $TEST_TMPDIR/zot.log + cat "$TEST_TMPDIR/zot.log" # wait until service is up count=5 up=0 @@ -295,6 +319,14 @@ function start_zot { exit 1 fi up=1 + # check if correct port is open + if ! nc -v -z "${ZOT_HOST}" "${ZOT_PORT}"; then + echo "no response from host:${ZOT_HOST} port:${ZOT_PORT}" >&3 + sleep 1 + count=$((count - 1)) + continue + fi + echo "Got response from host:${ZOT_HOST} on port:${ZOT_PORT}" >&3 if [[ -n $ZOT_USE_TLS ]]; then echo "testing zot at https://$ZOT_HOST:$ZOT_PORT" curl -v --cacert $BATS_SUITE_TMPDIR/ca.crt -u "iam:careful" -f https://$ZOT_HOST:$ZOT_PORT/v2/ || up=0 @@ -323,11 +355,14 @@ function start_zot { } function zot_teardown { - echo "# stopping zot" >&3 - killall zot - killall -KILL zot || true - rm -f $TEST_TMPDIR/zot-config.json - rm -rf $TEST_TMPDIR/zot + zotpid="${TEST_TMPDIR}/zot.pid" + if [ -s "$zotpid" ]; then + echo "# stopping zot" >&3 + pkill --pidfile "$zotpid" + echo "# stopped zot" >&3 + fi + rm -f "$TEST_TMPDIR/zot-config.json" + rm -rf "$TEST_TMPDIR/zot" } function _skopeo() { @@ -348,6 +383,77 @@ function _skopeo() { HOME="$home" skopeo "$@" } +function start_registry() { + if [ -z "${REGISTRY_URL}" ]; then + echo "Missing REGISTRY_URL env value" + return 1 + fi + if [ -z "${REGISTRY_SERVICE}" ]; then + echo "Missing REGISTRY_SERVICE env value" + return 1 + fi + rhost=${REGISTRY_URL%:*} # trim from right until colon, localhost + rport=${REGISTRY_URL#*:} # trim from left until colon , 5000 + if nc -v -z "${rhost}" "${rport}"; then + echo "# skipping start, registry service ${REGISTRY_SERVICE} already active for REGISTRY_URL=${REGISTRY_URL}" >&3 + return 0 + fi + echo "# no registry service ${REGISTRY_SERVICE} active for REGISTRY_URL=${REGISTRY_URL}" >&3 + echo "# Starting registry service ${REGISTRY_SERVICE}" >&3 + + + imgname=$(basename "${REGISTRY_SERVICE}") # registry:tag + if ! _skopeo copy "docker://${REGISTRY_SERVICE}" "oci:${TEST_TMPDIR}/ocid:${imgname}"; then + echo "# skopeo copy of '${REGISTRY_SERVICE}' to local directory failed" >&3 + return 1 + fi + + # unpack the image + unpackdir="${TEST_TMPDIR}/unpacked-reg" + if [ ! -d "${unpackdir}" ]; then + if ! umoci unpack --keep-dirlinks --image "${TEST_TMPDIR}/ocid:${imgname}" "${unpackdir}"; then + echo "failed to unpack registry service image ${imgname} to ${unpackdir}" + return 1 + fi + else + echo "# reusing existing unpacked registry service OCI image" >&3 + fi + + # start up registry in the background + reglog="${TEST_TMPDIR}/registry.log" + chroot "${unpackdir}/rootfs" "/entrypoint.sh" "/etc/docker/registry/config.yml" "2>&1" "1>${reglog}" & + REGISTRY_PID=$! + echo "$REGISTRY_PID" > "${TEST_TMPDIR}/registry.pid" + + regup=0 + for ((x=1;x<=5;x++)); do + sleep "$x" + if ! nc -v -z "${rhost}" "${rport}"; then + echo "# local registry service not ready" >&3 + sleep "$x" + continue + fi + regup=1 + break + done + + if [ "$regup" != "1" ]; then + echo "failed to bring up local registry service" + fi + + echo "local registry service up on ${REGISTRY_URL} PID=$(cat "${TEST_TMPDIR}/registry.pid")" + return 0 +} + +function stop_registry() { + regpid="${TEST_TMPDIR}/registry.pid" + if [ -s "$regpid" ]; then + echo "# stopping local registry" >&3 + pkill --pidfile "$regpid" + echo "# stopped local registry" >&3 + fi +} + function dir_has_only() { local d="$1" oifs="$IFS" unexpected="" f="" shift diff --git a/test/main.py b/test/main.py index 52c4a7d8..144f70db 100755 --- a/test/main.py +++ b/test/main.py @@ -1,4 +1,7 @@ #!/usr/bin/python3 +""" +test harness for stacker +""" import argparse import glob @@ -7,7 +10,24 @@ import subprocess import sys -priv_levels=("priv", "unpriv") + +def check_env(env_to_check): + """ + check for required env variables + """ + required_vars = ["ZOT_HOST", "ZOT_PORT", "REGISTRY_SERVICE", "REGISTRY_URL"] + errors = [] + for req_var in required_vars: + if req_var not in env_to_check: + errors.append(f"missing env variable '{req_var}'") + if not env_to_check.get(req_var): + errors.append(f"env variable '{req_var}' is empyty") + + if len(errors) > 0: + raise RuntimeError(f"EnvCheckFailures: {errors}") + + +priv_levels = ("priv", "unpriv") parser = argparse.ArgumentParser() parser.add_argument("--privilege-level", choices=priv_levels) @@ -16,17 +36,31 @@ options = parser.parse_args() -priv_to_test=priv_levels +priv_to_test = priv_levels if options.privilege_level is not None: priv_to_test = [options.privilege_level] for priv in priv_to_test: - cmd = ["bats", "--setup-suite-file", "./test/setup_suite.bash", "--jobs", str(options.jobs), "--tap", "--timing"] + cmd = [ + "bats", + "--setup-suite-file", + "./test/setup_suite.bash", + "--jobs", + str(options.jobs), + "--tap", + "--timing", + "--verbose-run", + ] cmd.extend(options.tests) env = os.environ.copy() env["PRIVILEGE_LEVEL"] = priv + try: + check_env + except RuntimeError as err: + print(f"Failed environment variable check: {err}") + sys.exit(1) print("running tests in modes:", priv) try: diff --git a/test/publish.bats b/test/publish.bats index b5b00d96..e6869991 100644 --- a/test/publish.bats +++ b/test/publish.bats @@ -1,5 +1,13 @@ load helpers +function setup_file() { + start_registry +} + +function teardown_file() { + stop_registry +} + function setup() { stacker_setup mkdir -p ocibuilds/sub1