mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-06 13:39:07 +02:00
* [self-hosted] Add a UBI image variant for the combined server OpenShift and other Red Hat environments expect UBI-based images that run as an arbitrary non-root UID. The proxy and rootless client already ship -ubi variants; this adds the same for netbird-server, published as <version>-ubi and ubi-latest for amd64 and arm64. * [self-hosted] Check the license output path before creating temp files The existing-output exit ran before the cleanup trap was registered, so it left the two mktemp files behind. * [self-hosted] Certify the netbird-server UBI image Adds netbird-server to the Red Hat certification components. Its Partner Connect component ID goes in the REDHAT_CERT_ID_NETBIRD_SERVER repository variable.
202 lines
8.1 KiB
YAML
202 lines
8.1 KiB
YAML
name: Red Hat Certification
|
|
|
|
# Certify published UBI images in the Red Hat Ecosystem Catalog. Called by
|
|
# release.yml on stable tags, or run by hand to (re)certify any released
|
|
# version. preflight submits every architecture of an image's manifest list
|
|
# to Pyxis; auto-publish on the component makes it public once certified.
|
|
#
|
|
# Each component's Partner Connect ID comes from the REDHAT_CERT_ID_<NAME>
|
|
# repository variable, e.g. REDHAT_CERT_ID_CLIENT_ROOTLESS. The run fails
|
|
# before certifying anything if a selected component's variable is not set.
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
component:
|
|
type: string
|
|
required: true
|
|
version:
|
|
type: string
|
|
required: true
|
|
secrets:
|
|
PYXIS_API_TOKEN:
|
|
required: true
|
|
workflow_dispatch:
|
|
inputs:
|
|
component:
|
|
description: "Component to certify"
|
|
type: choice
|
|
required: true
|
|
default: all
|
|
options:
|
|
- all
|
|
- client-rootless
|
|
- reverse-proxy
|
|
- netbird-server
|
|
version:
|
|
description: "Released version, e.g. v0.80.0"
|
|
type: string
|
|
required: true
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
resolve:
|
|
name: Resolve components
|
|
runs-on: ubuntu-24.04
|
|
outputs:
|
|
version: ${{ steps.resolve.outputs.version }}
|
|
matrix: ${{ steps.resolve.outputs.matrix }}
|
|
steps:
|
|
- name: Resolve components and images
|
|
id: resolve
|
|
env:
|
|
COMPONENT: ${{ inputs.component }}
|
|
INPUT_VERSION: ${{ inputs.version }}
|
|
REPO_VARS: ${{ toJSON(vars) }}
|
|
run: |
|
|
set -euo pipefail
|
|
version="${INPUT_VERSION#v}"
|
|
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
echo "::error::Only stable x.y.z versions are certified, got '${INPUT_VERSION}'"
|
|
exit 1
|
|
fi
|
|
# name, image repository, tag suffix (must match .goreleaser.yaml).
|
|
# Keep the names in sync with the workflow_dispatch options above.
|
|
components=(
|
|
"client-rootless ghcr.io/netbirdio/netbird -rootless-ubi"
|
|
"reverse-proxy ghcr.io/netbirdio/reverse-proxy -ubi"
|
|
"netbird-server ghcr.io/netbirdio/netbird-server -ubi"
|
|
)
|
|
matrix="[]"
|
|
missing=()
|
|
for c in "${components[@]}"; do
|
|
read -r name repo suffix <<< "$c"
|
|
[[ "$COMPONENT" == all || "$COMPONENT" == "$name" ]] || continue
|
|
var="REDHAT_CERT_ID_${name^^}"; var="${var//-/_}"
|
|
id="$(jq -r --arg v "$var" '.[$v] // empty' <<< "$REPO_VARS")"
|
|
if [[ -z "$id" ]]; then
|
|
missing+=("$var")
|
|
continue
|
|
fi
|
|
matrix="$(jq -c --arg n "$name" --arg t "${version}${suffix}" --arg r "${repo}:${version}${suffix}" --arg i "$id" \
|
|
'. + [{component: $n, tag: $t, ref: $r, component_id: $i}]' <<< "$matrix")"
|
|
done
|
|
if (( ${#missing[@]} )); then
|
|
echo "::error::Set these repository variables to the Partner Connect component IDs: ${missing[*]}"
|
|
exit 1
|
|
fi
|
|
if [[ "$matrix" == "[]" ]]; then
|
|
echo "::error::No component to certify for '${COMPONENT}'"
|
|
exit 1
|
|
fi
|
|
echo "Components to certify: ${matrix}"
|
|
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
|
echo "matrix=${matrix}" >> "$GITHUB_OUTPUT"
|
|
|
|
certify:
|
|
name: "Certify ${{ matrix.component }} UBI image"
|
|
needs: resolve
|
|
runs-on: ubuntu-24.04
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include: ${{ fromJSON(needs.resolve.outputs.matrix) }}
|
|
env:
|
|
PREFLIGHT_VERSION: "1.21.0"
|
|
# sha256 of preflight-linux-amd64 from the 1.21.0 GitHub release.
|
|
# Red Hat publishes no checksum file, so the value is pinned here.
|
|
PREFLIGHT_SHA256: "5e653135503c72f8702bbe31d7643197d12937c68086879133dd6b9650a9a449"
|
|
steps:
|
|
- name: Verify the multi-arch image is on ghcr.io
|
|
env:
|
|
IMAGE_REF: ${{ matrix.ref }}
|
|
run: |
|
|
set -euo pipefail
|
|
docker buildx imagetools inspect "$IMAGE_REF" --raw > manifest.json
|
|
for arch in amd64 arm64; do
|
|
if ! jq -e --arg a "$arch" '.manifests[] | select(.platform.architecture == $a)' manifest.json > /dev/null; then
|
|
echo "::error::${IMAGE_REF} has no ${arch} manifest"
|
|
exit 1
|
|
fi
|
|
done
|
|
echo "Manifest list for ${IMAGE_REF}:"
|
|
jq -r '.manifests[] | "\(.platform.os)/\(.platform.architecture) \(.digest)"' manifest.json
|
|
|
|
- name: Install preflight
|
|
run: |
|
|
set -euo pipefail
|
|
curl -fsSL --proto '=https' --proto-redir '=https' -o preflight \
|
|
"https://github.com/redhat-openshift-ecosystem/openshift-preflight/releases/download/${PREFLIGHT_VERSION}/preflight-linux-amd64"
|
|
echo "${PREFLIGHT_SHA256} preflight" | sha256sum -c -
|
|
chmod +x preflight
|
|
./preflight --version
|
|
|
|
- name: Run preflight checks and submit to Red Hat
|
|
env:
|
|
IMAGE_REF: ${{ matrix.ref }}
|
|
PFLT_PYXIS_API_TOKEN: ${{ secrets.PYXIS_API_TOKEN }}
|
|
PFLT_CERTIFICATION_COMPONENT_ID: ${{ matrix.component_id }}
|
|
PFLT_ARTIFACTS: artifacts
|
|
PFLT_LOGFILE: artifacts/preflight.log
|
|
PFLT_LOGLEVEL: info
|
|
PFLT_JUNIT: "true"
|
|
run: |
|
|
set -euo pipefail
|
|
# No --platform: preflight walks the manifest list and submits every
|
|
# architecture in one run, grouped under one manifest-list digest.
|
|
# preflight does not create the PFLT_LOGFILE directory, and --submit
|
|
# fails if the log file is missing.
|
|
mkdir -p artifacts
|
|
./preflight check container "$IMAGE_REF" --submit
|
|
|
|
- name: Fail if any check did not pass
|
|
run: |
|
|
set -euo pipefail
|
|
shopt -s nullglob
|
|
results=(artifacts/results.json artifacts/*/results.json)
|
|
if [[ ${#results[@]} -eq 0 ]]; then
|
|
echo "::error::preflight produced no results.json"
|
|
exit 1
|
|
fi
|
|
status=0
|
|
for f in "${results[@]}"; do
|
|
arch="$(basename "$(dirname "$f")")"
|
|
passed="$(jq -r '.passed' "$f")"
|
|
failed="$(jq -r '[.results.failed[]?.name] | join(", ")' "$f")"
|
|
echo "${arch}: passed=${passed} ${failed:+failed checks: ${failed}}"
|
|
[[ "$passed" == "true" ]] || status=1
|
|
done
|
|
exit $status
|
|
|
|
- name: Upload preflight artifacts
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: redhat-preflight-${{ matrix.component }}-${{ needs.resolve.outputs.version }}
|
|
path: artifacts/
|
|
retention-days: 30
|
|
|
|
- name: Wait for Pyxis to mark both architectures certified
|
|
env:
|
|
TAG: ${{ matrix.tag }}
|
|
COMPONENT_ID: ${{ matrix.component_id }}
|
|
PFLT_PYXIS_API_TOKEN: ${{ secrets.PYXIS_API_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
# Filter on the tag server-side so older versions are found past the first page.
|
|
url="https://catalog.redhat.com/api/containers/v1/projects/certification/id/${COMPONENT_ID}/images?filter=repositories.tags.name==${TAG}&page_size=100"
|
|
for attempt in $(seq 1 20); do
|
|
certified="$(curl -fsS --proto '=https' --proto-redir '=https' -H "X-API-KEY: ${PFLT_PYXIS_API_TOKEN}" "$url" \
|
|
| jq -r --arg t "$TAG" '[.data[] | select(.repositories[]?.tags[]?.name == $t) | select(.certified == true) | .architecture] | unique | join(",")')"
|
|
echo "attempt ${attempt}: certified architectures for ${TAG}: ${certified:-none}"
|
|
if [[ "$certified" == "amd64,arm64" ]]; then
|
|
echo "Both architectures certified. Auto-publish is enabled on the component, so the catalog updates on its own."
|
|
exit 0
|
|
fi
|
|
sleep 30
|
|
done
|
|
echo "::error::Pyxis has not marked both architectures certified after 10 minutes. Check https://connect.redhat.com/component/view/${COMPONENT_ID}/images"
|
|
exit 1
|