diff --git a/.github/actions/build-push-image/action.yaml b/.github/actions/build-push-image/action.yaml index fda56c152a..8dfc16e99b 100644 --- a/.github/actions/build-push-image/action.yaml +++ b/.github/actions/build-push-image/action.yaml @@ -57,7 +57,8 @@ runs: - name: "Set up Docker Buildx" uses: docker/setup-buildx-action@v3 with: - platforms: ${{ inputs.platforms }} + driver: docker-container + use: true # Devbox setup steps - name: Install devbox @@ -99,3 +100,5 @@ runs: sbom: true push: true tags: ${{ inputs.tags }} + labels: | + org.opencontainers.image.revision=${{ github.sha }} diff --git a/.github/actions/gen-install-scripts/Dockerfile b/.github/actions/gen-install-scripts/Dockerfile index 4c86eda4f4..a3eff82804 100644 --- a/.github/actions/gen-install-scripts/Dockerfile +++ b/.github/actions/gen-install-scripts/Dockerfile @@ -1,17 +1,22 @@ FROM golang:1.24 ENV KUBECTL_VERSION 1.18.12 +ENV KUSTOMIZE_VERSION 5.7.0 ENV GO111MODULE on RUN go install github.com/mikefarah/yq/v4@latest -# Install +# Install kubectl RUN curl -L https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl -o /usr/bin/kubectl && \ chmod +x /usr/bin/kubectl -RUN cd /usr/local/bin &&\ - curl -L https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh | bash +# Install kustomize (safe, pinned, prebuilt binary) +RUN curl -L https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv${KUSTOMIZE_VERSION}/kustomize_v${KUSTOMIZE_VERSION}_linux_amd64.tar.gz \ + -o /tmp/kustomize.tar.gz && \ + tar -xzvf /tmp/kustomize.tar.gz -C /usr/local/bin && \ + chmod +x /usr/local/bin/kustomize +# Install controller-gen RUN CONTROLLER_GEN_TMP_DIR=$(mktemp -d) && \ cd $CONTROLLER_GEN_TMP_DIR && \ go mod init tmp && \ @@ -19,12 +24,12 @@ RUN CONTROLLER_GEN_TMP_DIR=$(mktemp -d) && \ rm -rf $CONTROLLER_GEN_TMP_DIR && \ CONTROLLER_GEN=${GOBIN}/controller-gen +# Install operator-sdk RUN curl -LO https://github.com/operator-framework/operator-sdk/releases/download/v1.34.1/operator-sdk_linux_amd64 && \ chmod +x operator-sdk_linux_amd64 && \ mv operator-sdk_linux_amd64 /usr/local/bin/operator-sdk -# Copies your code file from your action repository to the filesystem path `/` of the container COPY entrypoint.sh /home/entrypoint.sh RUN chmod +x /home/entrypoint.sh -# Code file to execute when the docker container starts up (`entrypoint.sh`) + ENTRYPOINT ["/home/entrypoint.sh"] diff --git a/.github/actions/image2commit/action.yml b/.github/actions/image2commit/action.yml new file mode 100644 index 0000000000..48484ff17c --- /dev/null +++ b/.github/actions/image2commit/action.yml @@ -0,0 +1,42 @@ +name: image2commit +description: Resolve full commit SHA from a promoted image tag. + +inputs: + register: + description: "Registry (e.g., docker.io, quay.io)" + required: true + repo: + description: "Repository path (e.g., andrpac/my-repo)" + required: true + image_sha: + description: "Short SHA or 'latest'" + required: true + +outputs: + commit_sha: + description: "Resolved full commit SHA" + value: ${{ steps.resolve.outputs.commit_sha }} +runs: + using: "composite" + steps: + - name: Install skopeo and jq + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y skopeo jq + + - name: Resolve commit SHA + id: resolve + shell: bash + run: | + chmod +x ${{ github.action_path }}/entrypoint.sh + full_sha=$(${{ + github.action_path + }}/entrypoint.sh \ + "${{ inputs.register }}" \ + "${{ inputs.repo }}" \ + "${{ inputs.image_sha }}" + ) + + echo "Raw full_sha: $full_sha" + echo "commit_sha=$full_sha" >> $GITHUB_OUTPUT diff --git a/.github/actions/image2commit/entrypoint.sh b/.github/actions/image2commit/entrypoint.sh new file mode 100644 index 0000000000..0d324e1513 --- /dev/null +++ b/.github/actions/image2commit/entrypoint.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Copyright 2025 MongoDB Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script retrives the git commit sha given an image sha + +set -euo pipefail + +registry="$1" +repo="$2" +image_sha="$3" + +if [[ "$image_sha" == "latest" ]]; then + tag="promoted-latest" +else + tag="promoted-${image_sha}" +fi + +full_image="${registry}/${repo}:${tag}" + +sha=$(skopeo inspect "docker://${full_image}" | jq -r '.Labels["org.opencontainers.image.revision"]') + +if [[ -z "$sha" || "$sha" == "null" ]]; then + echo "Error: Could not extract commit SHA from $full_image" >&2 + exit 1 +fi + +echo "$sha" diff --git a/.github/actions/set-tag/action.yml b/.github/actions/set-tag/action.yml index 7f2c65058f..174b48c782 100644 --- a/.github/actions/set-tag/action.yml +++ b/.github/actions/set-tag/action.yml @@ -1,8 +1,18 @@ name: 'Setup tag for docker image' -description: 'Setup tag for docker image: branch name with commit ID' +description: 'Generates a Docker image tag using branch name and short commit SHA.' +inputs: + branch_name: + description: 'Branch name to use for the tag (e.g., main, feature-xyz). Optional.' + required: false + commit_sha: + description: 'Full commit SHA to extract the short commit ID from. Optional.' + required: false + outputs: tag: - description: 'tag for the image' + description: 'Generated image tag in the format {branch-name}-{6-char-sha}' + runs: using: 'docker' image: 'Dockerfile' + args: [] diff --git a/.github/actions/set-tag/entrypoint.sh b/.github/actions/set-tag/entrypoint.sh index fadde623c0..e2978dcd85 100644 --- a/.github/actions/set-tag/entrypoint.sh +++ b/.github/actions/set-tag/entrypoint.sh @@ -13,17 +13,31 @@ # See the License for the specific language governing permissions and # limitations under the License. - -#set -eou pipefail +set -eou pipefail git config --global --add safe.directory /github/workspace -# Setup tag name -commit_id=$(git rev-parse --short HEAD) -branch_name=${GITHUB_HEAD_REF-} -if [ -z "${branch_name}" ]; then - branch_name=$(echo "$GITHUB_REF" | awk -F'/' '{print $3}') +# Get the full commit hash and shorten to 6 characters +full_commit_sha="${INPUT_COMMIT_SHA:-}" +if [ -z "$full_commit_sha" ]; then + full_commit_sha=$(git rev-parse HEAD) +fi +commit_id=$(echo "$full_commit_sha" | cut -c1-6) + +# Get the full branch name +branch_name="${INPUT_BRANCH_NAME:-}" +if [ -z "$branch_name" ]; then + if [ -n "$GITHUB_HEAD_REF" ]; then + branch_name="$GITHUB_HEAD_REF" + else + branch_name="${GITHUB_REF#refs/heads/}" + fi fi -branch_name=$(echo "${branch_name}" | awk '{print substr($0, 1, 15)}' | sed 's/\//-/g; s/\./-/g') + +# Replace / and . with - +# Then truncate to 15 characters +branch_name=$(echo "$branch_name" | sed 's/[\/\.]/-/g' | awk '{print substr($0, 1, 15)}') + +# Create tag as {branch_name}-{6-digit-commit} tag="${branch_name}-${commit_id}" -echo "tag=$tag" >> "$GITHUB_OUTPUT" +echo "tag=${tag}" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/cloud-tests-filter.yml b/.github/workflows/cloud-tests-filter.yml index dd6eca1628..0fb34f7be7 100644 --- a/.github/workflows/cloud-tests-filter.yml +++ b/.github/workflows/cloud-tests-filter.yml @@ -55,7 +55,7 @@ jobs: ACTOR: ${{ github.actor }} run: | # Evaluate whether or not cloud tests should run - RUN_CLOUD_TESTS='false' + RUN_CLOUD_TESTS='true' # Scheduled runs on default branch always run all tests if [ "${EVENT}" == "schedule" ];then RUN_CLOUD_TESTS='true' diff --git a/.github/workflows/cloud-tests.yml b/.github/workflows/cloud-tests.yml index 96acacc964..4a7cca70cc 100644 --- a/.github/workflows/cloud-tests.yml +++ b/.github/workflows/cloud-tests.yml @@ -13,31 +13,15 @@ jobs: runs-on: ubuntu-latest if: | github.event_name == 'workflow_dispatch' || - github.event_name == 'merge_group' || + github.event_name == 'schedule' || github.ref == 'refs/heads/main' || (github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]') || contains(github.event.pull_request.labels.*.name, 'safe-to-test') steps: - - name: allowed message + - name: allowed messager run: echo "Allowed to run" - int-tests: - needs: allowed - uses: ./.github/workflows/test-int.yml - secrets: inherit - e2e-tests: needs: allowed uses: ./.github/workflows/test-e2e.yml secrets: inherit - - test-e2e-gov: - needs: - - allowed - uses: ./.github/workflows/test-e2e-gov.yml - secrets: inherit - - openshift-upgrade-test: - needs: allowed - uses: ./.github/workflows/openshift-upgrade-test.yaml - secrets: inherit diff --git a/.github/workflows/promote-image.yml b/.github/workflows/promote-image.yml new file mode 100644 index 0000000000..f03d47d680 --- /dev/null +++ b/.github/workflows/promote-image.yml @@ -0,0 +1,77 @@ +name: Promote Image + +on: + workflow_run: + workflows: ["Test"] + types: [completed] + +jobs: + promote-image: + runs-on: ubuntu-latest + environment: release + if: | + github.event.workflow_run.head_branch == 'main' && + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.event == 'schedule' + env: + GHCR_REPO: ghcr.io/andrpac/mongodb-atlas-kubernetes-operator-prerelease + DOCKER_REPO: docker.io/andrpac/mongodb-atlas-kubernetes-operator-prerelease + QUAY_REPO: quay.io/andrpac/mongodb-atlas-kubernetes-operator-prerelease + steps: + - name: Checkout PR commit + uses: actions/checkout@v4 + + - name: Log in to the GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Login to Docker registry + uses: docker/login-action@v3 + with: + registry: docker.io + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Log in to Quay registry + uses: docker/login-action@v3 + with: + registry: quay.io + username: ${{ secrets.QUAY_USERNAME }} + password: ${{ secrets.QUAY_PASSWORD }} + + - name: Prepare image tag + id: set_tag + uses: ./.github/actions/set-tag + with: + branch_name: ${{ github.event.workflow_run.head_branch }} + commit_sha: ${{ github.event.workflow_run.head_sha }} + + - name: Prepare tag for promoted image + id: promoted_tag + run: | + RAW_TAG="${{ steps.set_tag.outputs.tag }}" + COMMIT_SHA="${RAW_TAG##*-}" + echo "tag=promoted-${COMMIT_SHA}" >> $GITHUB_OUTPUT + + - name: Move image to Docker Hub + run: ./scripts/move-image.sh + env: + IMAGE_SRC_REPO: ${{ env.GHCR_REPO }} + IMAGE_DEST_REPO: ${{ env.DOCKER_REPO }} + IMAGE_SRC_TAG: ${{ steps.set_tag.outputs.tag }} + IMAGE_DEST_TAG: ${{ steps.promoted_tag.outputs.tag }} + ALIAS_ENABLED: true + ALIAS_TAG: promoted-latest + + - name: Move image to Quay + run: ./scripts/move-image.sh + env: + IMAGE_SRC_REPO: ${{ env.GHCR_REPO }} + IMAGE_DEST_REPO: ${{ env.QUAY_REPO }} + IMAGE_SRC_TAG: ${{ steps.set_tag.outputs.tag }} + IMAGE_DEST_TAG: ${{ steps.promoted_tag.outputs.tag }} + ALIAS_ENABLED: true + ALIAS_TAG: promoted-latest diff --git a/.github/workflows/release-image.yml b/.github/workflows/release-image.yml new file mode 100644 index 0000000000..f4bbb75379 --- /dev/null +++ b/.github/workflows/release-image.yml @@ -0,0 +1,443 @@ +name: Release Image + +on: + workflow_dispatch: + inputs: + version: + description: "Release version (e.g. v1.2.3)" + required: true + type: string + authors: + description: "Comma-separated list of author emails (e.g. johnsmith@mongodb.com)" + required: true + type: string + image_sha: + description: "6-digit commit SHA used for the promoted image (e.g. 3e79a3 or 'latest')" + required: false + default: "latest" + type: string + push: + branches: + - '**' + +permissions: + contents: write + pull-requests: write + + +jobs: + # Image2commit: Creates a mapping between the image_sha given as input and the actual git commit + # This is necassary for the release-image step that requires checking out that exact git commit + image2commit: + name: Resolve Commit SHA from Image + runs-on: ubuntu-latest + environment: release + outputs: + commit_sha: ${{ steps.resolve.outputs.commit_sha }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Log in to Docker registry + uses: docker/login-action@v3 + with: + registry: docker.io + username: ${{ secrets.ANDRPAC_DOCKER_USERNAME }} + password: ${{ secrets.ANDRPAC_DOCKER_PASSWORD }} + + - name: Run image2commit + id: resolve + uses: ./.github/actions/image2commit + with: + register: docker.io + repo: andrpac/mongodb-atlas-kubernetes-operator-prerelease + image_sha: latest # !!!!!!!!!!!!!!!! To make the acutal input later !!!!!! + + check-commit: + name: Check resolved commit + runs-on: ubuntu-latest + needs: image2commit + steps: + - name: Echo resolved commit + run: | + echo "Resolved commit: ${{ needs.image2commit.outputs.commit_sha }}" + + # Release-image: Created and uploads a release for the specified operator version given in the image_sha + # Note, with new releases, all of the release artifacts will be stored withing docs/releases/{release_version} + release-image: + runs-on: ubuntu-latest + environment: release + env: + VERSION: ${{ github.event.inputs.version || 'vtest-0.0.0-dev' }} + AUTHORS: ${{ github.event.inputs.authors || 'unknown' }} + IMAGE_SHA: ${{ github.event.inputs.image_sha || 'latest' }} + DOCKER_SIGNATURE_REPO: docker.io/andrpac/signatures + DOCKER_RELEASE_REPO: docker.io/andrpac/mongodb-atlas-kubernetes-operator + DOCKER_PRERELEASE_REPO: docker.io/andrpac/mongodb-atlas-kubernetes-operator-prerelease + QUAY_RELEASE_REPO: quay.io/andrpac/mongodb-atlas-kubernetes-operator + QUAY_PRERELEASE_REPO: quay.io/andrpac/mongodb-atlas-kubernetes-operator-prerelease + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + #ref: $#{{ needs.image2commit.outputs.commit_sha }} !!!!!!!!! SUPER IMPORTNAT TO PUT BACK !!!!!!! + + - name: Generate GitHub App Token + id: generate_token + uses: mongodb/apix-action/token@v8 + with: + app-id: ${{ secrets.AKO_RELEASER_APP_ID }} + private-key: ${{ secrets.AKO_RELEASER_RSA_KEY }} + + # Login in into all registries + - name: Log in to Docker registry + uses: docker/login-action@v3 + with: + registry: docker.io + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Log in to Quay registry + uses: docker/login-action@v3 + with: + registry: quay.io + username: ${{ secrets.QUAY_USERNAME }} + password: ${{ secrets.QUAY_PASSWORD }} + + - name: Log in to Artifactory + uses: docker/login-action@v3 + with: + registry: artifactory.corp.mongodb.com + username: ${{ secrets.MDB_ARTIFACTORY_USERNAME }} + password: ${{ secrets.MDB_ARTIFACTORY_PASSWORD }} + + # This step configures all of the dynamic variables needed for later steps + - name: Configure job environment for downstream steps + id: tags + run: | + promoted_tag="promoted-${IMAGE_SHA}" + release_tag="${VERSION}" + certified_tag="certified-${release_tag}" + + docker_image_url="${DOCKER_RELEASE_REPO}:${release_tag}" + quay_image_url="${QUAY_RELEASE_REPO}:${release_tag}" + quay_certified_image_url="${QUAY_RELEASE_REPO}:${certified_tag}" + + echo "promoted_tag=$promoted_tag" >> $GITHUB_OUTPUT + echo "release_tag=$release_tag" >> $GITHUB_OUTPUT + echo "certified_tag=$certified_tag" >> $GITHUB_OUTPUT + echo "docker_image_url=$docker_image_url" >> $GITHUB_OUTPUT + echo "quay_image_url=$quay_image_url" >> $GITHUB_OUTPUT + echo "quay_certified_image_url=$quay_certified_image_url" >> $GITHUB_OUTPUT + + # Move prerelease images to official release registries in Docker Hub and Quay + + # Link updates to pr: all-in-one.yml, helm-updates, sdlc requirements + - name: Generate deployment configurations + uses: ./.github/actions/gen-install-scripts + with: + ENV: prod + IMAGE_URL: ${{ steps.tags.outputs.docker_image_url }} + + - name: Bump Helm chart version + run: ./scripts/bump-helm-chart-version.sh + + # Prepare SDLC requirement: signatures, sboms, compliance reports + # Note, signed images will live in mongodb/release and mongodb/signature repos + + - name: Create SDLC report + run: make gen-sdlc-checklist + + # Create PR on release branch with all updates generated + - name: Create release pr with all updated artefacts + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + export BRANCH="new-release/${VERSION}" + export COMMIT_MESSAGE="feat: release ${VERSION}" + export RELEASE_DIR="releases/${VERSION}" + export SOURCE_COMMIT=$(git rev-parse HEAD) + + git config --global user.name "${{ steps.generate_token.outputs.user-name }}" + git config --global user.email "${{ steps.generate_token.outputs.user-email }}" + + mkdir -p "$RELEASE_DIR" + mv deploy "$RELEASE_DIR/deploy" + mv bundle "$RELEASE_DIR/bundle" + mv helm-charts "$RELEASE_DIR/helm-charts" + mv bundle.Dockerfile "$RELEASE_DIR/bundle.Dockerfile" + + git fetch origin + git checkout -b "$BRANCH" origin/main + git push -f origin "$BRANCH" + + git add -f "$RELEASE_DIR" + scripts/create-signed-commit.sh + + gh pr create \ + --draft \ + --base main \ + --head "$BRANCH" \ + --title "$COMMIT_MESSAGE" \ + --body "This is an autogenerated PR to prepare for the release" + + git checkout $SOURCE_COMMIT + + # Create release artefacts on GitHub + - name: Create configuration package + run: | + set -x + tar czvf atlas-operator-all-in-one-${{ env.VERSION }}.tar.gz -C deploy all-in-one.yaml + + - name: Tag the release assets + run: | + git fetch --tags + git tag -f ${{ env.VERSION }} + git push -f origin ${{ env.VERSION }} + + - name: Create release on GitHub + uses: softprops/action-gh-release@v2 + with: + draft: true + prerelease: false + tag_name: "${{ env.VERSION }}" + name: "${{ env.VERSION }}" + token: ${{ secrets.GITHUB_TOKEN }} + body_path: docs/release-notes/release-notes-template.md + files: | + ./atlas-operator-all-in-one-${{ env.VERSION }}.tar.gz + ./docs/releases/v${{ env.VERSION }}/sdlc-compliance.md + ./docs/releases/v${{ env.VERSION }}/linux-amd64.sbom.json + ./docs/releases/v${{ env.VERSION }}/linux-arm64.sbom.json + + prepare-environment: + name: Set up Environment Variables + runs-on: ubuntu-latest + if: false + environment: release + + outputs: + # Inputs + version: ${{ steps.setup.outputs.version }} + authors: ${{ steps.setup.outputs.authors }} + commit_sha: ${{ steps.setup.outputs.commit_sha }} + + # Release related + release_commit: ${{ steps.setup.outputs.release_commit }} + release_branch: ${{ steps.setup.outputs.release_branch }} + + # Tags + promoted_tag: ${{ steps.setup.outputs.promoted_tag }} + release_tag: ${{ steps.setup.outputs.release_tag }} + certified_tag: ${{ steps.setup.outputs.certified_tag }} + + # Repos + docker_prerelease_repo: ${{ steps.setup.outputs.docker_prerelease_repo }} + docker_release_repo: ${{ steps.setup.outputs.docker_release_repo }} + docker_signature_repo: ${{ steps.setup.outputs.docker_signature_repo }} + quay_prerelease_repo: ${{ steps.setup.outputs.quay_prerelease_repo }} + quay_release_repo: ${{ steps.setup.outputs.quay_release_repo }} + + # Image URLs + docker_image_url: ${{ steps.setup.outputs.docker_image_url }} + quay_image_url: ${{ steps.setup.outputs.quay_image_url }} + quay_certified_image_url: ${{ steps.setup.outputs.quay_certified_image_url }} + + steps: + - name: Log in to Docker registry + uses: docker/login-action@v3 + with: + registry: docker.io + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Resolve inputs + id: inputs + run: | + echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT + echo "authors=${{ github.event.inputs.authors }}" >> $GITHUB_OUTPUT + echo "image_sha=${{ github.event.inputs.image_sha }}" >> $GITHUB_OUTPUT + + - name: Resolve commit SHA from image + id: image2commit + uses: ./.github/actions/image2commit + with: + image_sha: ${{ steps.inputs.outputs.image_sha }} + repo: docker.io/andrpac/mongodb-atlas-kubernetes-operator-prerelease + + - name: Set derived environment variables + id: setup + run: | + version="${{ steps.inputs.outputs.version }}" + authors="${{ steps.inputs.outputs.authors }}" + sha="${{ steps.image2commit.outputs.commit_sha }}" + + docker_prerelease_repo="docker.io/andrpac/mongodb-atlas-kubernetes-operator-prerelease" + docker_release_repo="docker.io/andrpac/mongodb-atlas-kubernetes-operator" + docker_signature_repo="docker.io/andrpac/signatures" + quay_prerelease_repo="quay.io/andrpac/mongodb-atlas-kubernetes-operator-prerelease" + quay_release_repo="quay.io/andrpac/mongodb-atlas-kubernetes-operator" + + short_sha="${sha:0:6}" + promoted_tag="promoted-${short_sha}" + + release_tag="$version" + certified_tag="certified-${version}" + release_branch="new-release/${version}" + docker_image_url="${docker_release_repo}:${release_tag}" + quay_image_url="${quay_release_repo}:${release_tag}" + quay_certified_image_url="${quay_release_repo}:${certified_tag}" + + echo "version=$version" >> $GITHUB_OUTPUT + echo "authors=$authors" >> $GITHUB_OUTPUT + echo "commit_sha=${{ steps.inputs.outputs.image_sha }}" >> $GITHUB_OUTPUT + echo "release_commit=$sha" >> $GITHUB_OUTPUT + echo "release_branch=$release_branch" >> $GITHUB_OUTPUT + echo "promoted_tag=$promoted_tag" >> $GITHUB_OUTPUT + echo "release_tag=$release_tag" >> $GITHUB_OUTPUT + echo "certified_tag=$certified_tag" >> $GITHUB_OUTPUT + echo "docker_prerelease_repo=$docker_prerelease_repo" >> $GITHUB_OUTPUT + echo "docker_release_repo=$docker_release_repo" >> $GITHUB_OUTPUT + echo "docker_signature_repo=$docker_signature_repo" >> $GITHUB_OUTPUT + echo "quay_prerelease_repo=$quay_prerelease_repo" >> $GITHUB_OUTPUT + echo "quay_release_repo=$quay_release_repo" >> $GITHUB_OUTPUT + echo "docker_image_url=$docker_image_url" >> $GITHUB_OUTPUT + echo "quay_image_url=$quay_image_url" >> $GITHUB_OUTPUT + echo "quay_certified_image_url=$quay_certified_image_url" >> $GITHUB_OUTPUT + + release-image1: + runs-on: ubuntu-latest + if: false + environment: release + env: + VERSION: ${{ github.event.inputs.version }} + AUTHORS: ${{ github.event.inputs.authors }} + DOCKER_RELEASE_REPO: docker.io/andrpac/mongodb-atlas-kubernetes-operator + DOCKER_PRERELEASE_REPO: docker.io/andrpac/mongodb-atlas-kubernetes-operator-prerelease + QUAY_RELEASE_REPO: quay.io/andrpac/mongodb-atlas-kubernetes-operator + QUAY_PRERELEASE_REPO: quay.io/andrpac/mongodb-atlas-kubernetes-operator-prerelease + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Generate GitHub App Token + id: generate_token + uses: mongodb/apix-action/token@v8 + with: + app-id: ${{ secrets.AKO_RELEASER_APP_ID }} + private-key: ${{ secrets.AKO_RELEASER_RSA_KEY }} + + - name: Log in to Docker registry + uses: docker/login-action@v3 + with: + registry: docker.io + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Log in to Quay registry + uses: docker/login-action@v3 + with: + registry: quay.io + username: ${{ secrets.QUAY_USERNAME }} + password: ${{ secrets.QUAY_PASSWORD }} + + - name: Install devbox + uses: jetify-com/devbox-install-action@v0.13.0 + + - name: Resolve commit SHA and promotion tag + id: tags + run: | + if [ "${{ github.event.inputs.commit_sha }}" = "latest" ]; then + git fetch origin main + sha=$(git rev-parse origin/main) + else + sha="${{ github.event.inputs.commit_sha }}" + fi + + short_sha="${sha:0:6}" + echo "promoted_tag=promoted-${short_sha}" >> "$GITHUB_OUTPUT" + + # Move prerelease images to official release registries in Docker Hub and Quay + - name: Move image to Docker registry release from prerelease + run: devbox run -- ./scripts/move-image.sh + env: + IMAGE_SRC_REPO: ${{ env.DOCKER_PRERELEASE_REPO }} + IMAGE_DEST_REPO: ${{ env.DOCKER_RELEASE_REPO }} + IMAGE_SRC_TAG: ${{ steps.tags.outputs.promoted_tag }} + IMAGE_DEST_TAG: ${{ github.event.inputs.version }} + + - name: Move image to Quay registry release from prerelease + run: devbox run -- ./scripts/move-image.sh + env: + IMAGE_SRC_REPO: ${{ env.QUAY_PRERELEASE_REPO }} + IMAGE_DEST_REPO: ${{ env.QUAY_RELEASE_REPO }} + IMAGE_SRC_TAG: ${{ steps.tags.outputs.promoted_tag }} + IMAGE_DEST_TAG: ${{ github.event.inputs.version }} + + # Create Openshift certified images + - name: Create OpenShift certified image on Quay + run: devbox run -- ./scripts/move-image.sh + env: + IMAGE_SRC_REPO: ${{ env.QUAY_PRERELEASE_REPO }} + IMAGE_DEST_REPO: ${{ env.QUAY_RELEASE_REPO }} + IMAGE_SRC_TAG: ${{ steps.tags.outputs.promoted_tag }} + IMAGE_DEST_TAG: ${{ steps.tags.outputs.certified_tag }} + + - name: Create deploy configurations + uses: ./.github/actions/gen-install-scripts + with: + ENV: prod + IMAGE_URL: "${{ env.DOCKER_RELEASE_REPO }}:${{ github.event.inputs.version }}" + + - name: Bump helm chart version + run: devbox run -- ./scripts/bump-helm-chart-version.sh + + - name: Login to artifactory + uses: docker/login-action@v3 + with: + registry: artifactory.corp.mongodb.com + username: ${{ secrets.MDB_ARTIFACTORY_USERNAME }} + password: ${{ secrets.MDB_ARTIFACTORY_PASSWORD }} + + - name: Sign Docker registry released images + run: devbox run -- make sign + env: + PKCS11_URI: ${{ secrets.PKCS11_URI }} + GRS_USERNAME: ${{ secrets.GRS_USERNAME }} + GRS_PASSWORD: ${{ secrets.GRS_PASSWORD }} + IMG_REPO: ${{ env.DOCKER_RELEASE_REPO }} + VERSION: ${{ github.event.inputs.version }} + + - name: Sign Quay registry release image + run: devbox run -- make sign + env: + PKCS11_URI: ${{ secrets.PKCS11_URI }} + GRS_USERNAME: ${{ secrets.GRS_USERNAME }} + GRS_PASSWORD: ${{ secrets.GRS_PASSWORD }} + IMG_REPO: ${{ env.QUAY_RELEASE_REPO }} + VERSION: ${{ github.event.inputs.version }} + + - name: Generate SBOMs + run: devbox run -- make generate-sboms + env: + RELEASED_OPERATOR_IMAGE: ${{ env.DOCKER_RELEASE_REPO }} + + - name: Create SDLC report + run: devbox run -- make gen-sdlc-checklist + + - name: Commit and create pull request for release changes + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ steps.generate_token.outputs.token }} + commit-message: "chore(release): updates from new release v${{ github.event.inputs.version }}" + title: "Release v${{ github.event.inputs.version }}" + body: | + This PR was automatically generated by the **release-image** workflow. + + Version: `${{ github.event.inputs.version }}` + Authors: ${{ github.event.inputs.authors }} + branch: "release/${{ github.event.inputs.version }}" + delete-branch: true diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index fcd875b247..71de182b6b 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -1,257 +1,123 @@ -name: E2E tests. +name: E2E tests on: workflow_call: workflow_dispatch: +env: + MCLI_OPS_MANAGER_URL: "https://cloud-qa.mongodb.com/" + MCLI_PUBLIC_API_KEY: ${{ secrets.ATLAS_PUBLIC_KEY }} + MCLI_PRIVATE_API_KEY: ${{ secrets.ATLAS_PRIVATE_KEY }} + MCLI_ORG_ID: ${{ secrets.ATLAS_ORG_ID }} + jobs: - compute: - name: "Compute test matrix" + prepare-e2e: runs-on: ubuntu-latest outputs: - test_matrix: ${{ steps.test.outputs.matrix }} + test_matrix: ${{ steps.compute.outputs.matrix }} steps: - - id: test - name: Compute Test Matrix + - uses: actions/checkout@v4 + + - name: Compute K8s matrix/versions for testing + id: compute run: | - # Note the use of external single quotes to allow for double quotes at inline YAML array matrix='["v1.30.10-kind"]' - if [ "${{ github.ref }}" == "refs/heads/main" ];then + if [ "${{ github.ref }}" == "refs/heads/main" ]; then matrix='["v1.30.10-kind", "v1.32.2-kind"]' fi echo "matrix=${matrix}" >> "${GITHUB_OUTPUT}" - cat "${GITHUB_OUTPUT}" - prepare-e2e: - name: Prepare E2E configuration and image - environment: release + prepare-e2e-image: runs-on: ubuntu-latest - env: - REPOSITORY: ${{ github.repository_owner }}/mongodb-atlas-kubernetes-operator-prerelease outputs: - tag: ${{ steps.prepare.outputs.tag }} - steps: - - name: Check out code - uses: actions/checkout@v4 - with: - ref: ${{github.event.pull_request.head.sha}} - submodules: true - fetch-depth: 0 - - - name: Prepare tag - id: prepare - uses: ./.github/actions/set-tag - - name: Log in to ghcr.io registry - run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin - - - name: Build and Push image - uses: ./.github/actions/build-push-image - with: - file: fast.Dockerfile - repository: ghcr.io/${{ env.REPOSITORY }} - version: ${{ steps.prepare.outputs.tag }} - tags: ghcr.io/${{ env.REPOSITORY }}:${{ steps.prepare.outputs.tag }} - platforms: linux/amd64 - push_to_docker: false - - - name: Do preflight-check on test image - uses: ./.github/actions/certify-openshift-images - with: - registry: ghcr.io - registry_password: ${{ secrets.GITHUB_TOKEN }} - repository: ${{ env.REPOSITORY }} - version: ${{ steps.prepare.outputs.tag }} - rhcc_token: ${{ secrets.RH_CERTIFICATION_PYXIS_API_TOKEN }} - rhcc_project: ${{ secrets.RH_CERTIFICATION_OSPID }} - submit: false - - prepare-e2e-bundle: - name: Prepare E2E Bundle configuration and image - runs-on: ubuntu-latest + image_url: ${{ steps.set_image_url.outputs.image_url }} env: - GHCR_REPO: ghcr.io/mongodb/mongodb-atlas-kubernetes-operator-prerelease - GHCR_BUNDLES_REPO: ghcr.io/mongodb/mongodb-atlas-kubernetes-bundles-prerelease + REPO: ghcr.io/${{ github.repository_owner }}/mongodb-atlas-kubernetes-operator-prerelease steps: - - name: Check out code - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: - ref: ${{github.event.pull_request.head.sha}} - submodules: true - fetch-depth: 0 - - - name: Prepare tag - id: prepare + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Prepare image tag + id: set_tag uses: ./.github/actions/set-tag - - - name: Generate configuration for the tests - uses: ./.github/actions/gen-install-scripts - with: - IMAGE_URL: ${{ env.GHCR_REPO }}:${{ steps.prepare.outputs.tag }} - VERSION: ${{ steps.prepare.outputs.tag }} - ENV: dev - - - name: Change path for the test + + - name: Prepare image url for GitHub Container Registry + id: set_image_url run: | - awk '{gsub(/cloud.mongodb.com/, "cloud-qa.mongodb.com", $0); print}' bundle/manifests/mongodb-atlas-kubernetes.clusterserviceversion.yaml > tmp && mv tmp bundle/manifests/mongodb-atlas-kubernetes.clusterserviceversion.yaml + echo "image_url=${REPO}:${{ steps.set_tag.outputs.tag }}" >> "$GITHUB_OUTPUT" - - name: Cache repo files - uses: actions/cache@v4 + - name: Log in to the GitHub Container Registry + uses: docker/login-action@v3 with: - path: | - ./* - key: ${{ github.sha }} - - - name: Prepare docker tag - id: prepare-docker-bundle-tag - run: | - REPOSITORY=${{ env.GHCR_BUNDLES_REPO }} - TAG=${{ steps.prepare.outputs.tag }} - TAGS="${REPOSITORY}:${TAG}" - echo "tags=$TAGS" >> $GITHUB_OUTPUT - - - name: Log in to ghcr.io registry - run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin - - - name: Build and Push image + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image to GitHub Container Registry uses: ./.github/actions/build-push-image with: file: fast.Dockerfile - repository: ${{ env.GHCR_BUNDLES_REPO }} - version: ${{ steps.prepare.outputs.tag }} - tags: ${{ env.GHCR_BUNDLES_REPO }}:${{ steps.prepare.outputs.tag }} - platforms: linux/amd64 + repository: ${{ env.REPO }} + version: ${{ steps.set_tag.outputs.tag }} + tags: ${{ steps.set_image_url.outputs.image_url }} + platforms: linux/amd64,linux/arm64 push_to_docker: false + e2e: - name: E2E tests - environment: test - needs: [compute, prepare-e2e, prepare-e2e-bundle] + needs: [prepare-e2e] runs-on: ubuntu-latest - env: - GHCR_REPO: ghcr.io/mongodb/mongodb-atlas-kubernetes-operator-prerelease - GHCR_BUNDLES_REPO: ghcr.io/mongodb/mongodb-atlas-kubernetes-bundles-prerelease + environment: test strategy: fail-fast: false matrix: - k8s: ${{fromJson(needs.compute.outputs.test_matrix)}} - test: + k8s: ${{ fromJson(needs.prepare-e2e.outputs.test_matrix) }} + test: [ - "alert-config", - "auditing", - # "bundle-test", - "cloud-access-role", - "deployment-annotations-ns", - "deployment-ns", - "users", - "users-oidc", - "deployment-wide", - "encryption-at-rest", "free-tier", - "global-deployment", - "helm-ns", - "helm-update", - "helm-wide", - "integration-ns", - "long-run", - "multinamespaced", - "networkpeering", - "privatelink", - "private-endpoint", - "project-settings", - "x509auth", - "custom-roles", - "teams", - "backup-config", - "datafederation", - "atlas-search-nodes", - "atlas-search-index", - "cache-watch", - "reconcile-all", - "reconcile-one", - "reconcile-two", - "backup-compliance", - "flex", - "ip-access-list", - "dry-run", - "networkcontainer-controller", - "networkpeering-controller", ] steps: - - name: Get repo files from cache - id: get-repo-files-from-cache - uses: actions/cache@v4 + - uses: actions/checkout@v4 with: - path: ./* - key: ${{ github.sha }} - - - name: Checkout if cache repo files missed - if: steps.get-repo-files-from-cache.outputs.cache-hit != 'true' - uses: actions/checkout@v4 - with: - ref: ${{github.event.pull_request.head.sha}} - submodules: true - fetch-depth: 0 - + ref: ${{ github.event.pull_request.head.sha || github.sha }} + - name: Install devbox uses: jetify-com/devbox-install-action@v0.13.0 with: enable-cache: 'true' - - - name: Set properties - id: properties - run: | - version=$(echo ${{ matrix.k8s }} | awk -F "-" '{print $1}') - platform=$(echo ${{ matrix.k8s }} | awk -F "-" '{print $2}') - echo "k8s_version=$version" >> $GITHUB_OUTPUT - echo "k8s_platform=$platform" >> $GITHUB_OUTPUT - - name: Generate configuration for the tests + - name: Generate kustomized all-in-one install configs uses: ./.github/actions/gen-install-scripts with: - IMAGE_URL: ${{ env.GHCR_REPO }}:${{ needs.prepare-e2e.outputs.tag }} - VERSION: ${{ needs.prepare-e2e.outputs.tag }} ENV: dev + VERSION: dev - - name: Change path for the test + - name: Extract k8s version/platform + id: extract run: | - awk '{gsub(/cloud.mongodb.com/, "cloud-qa.mongodb.com", $0); print}' bundle/manifests/mongodb-atlas-kubernetes.clusterserviceversion.yaml > tmp && mv tmp bundle/manifests/mongodb-atlas-kubernetes.clusterserviceversion.yaml + echo "k8s_version=$(echo '${{ matrix.k8s }}' | awk -F '-' '{print $1}')" >> $GITHUB_OUTPUT + echo "k8s_platform=$(echo '${{ matrix.k8s }}' | awk -F '-' '{print $2}')" >> $GITHUB_OUTPUT - - name: Create k8s Kind Cluster - if: ${{ steps.properties.outputs.k8s_platform == 'kind' && !env.ACT }} + - name: Setup kind cluster + if: ${{ steps.extract.outputs.k8s_platform == 'kind' }} uses: helm/kind-action@v1.12.0 with: version: v0.26.0 config: test/helper/e2e/config/kind.yaml - node_image: kindest/node:${{ steps.properties.outputs.k8s_version }} - cluster_name: ${{ matrix.test }} + node_image: kindest/node:${{ steps.extract.outputs.k8s_version }} + cluster_name: ${{ matrix.test }}-${{ matrix.k8s }} wait: 180s - - name: Print kubectl version - run: | - devbox run -- 'kubectl version' + - name: Print Kubernetes version + run: devbox run -- kubectl version - - name: Print kubectl version - run: | - devbox run -- 'kubectl version' + - name: Apply CRDs + run: devbox run -- kubectl apply -f deploy/crds - - name: Install CRDs if needed - if: ${{ !( matrix.test == 'helm-update' || matrix.test == 'helm-wide' || matrix.test == 'helm-ns' || matrix.test == 'bundle-test' ) }} - run: | - devbox run -- 'kubectl apply -f deploy/crds' - - - name: Run e2e test + - name: Run CI E2E tests + run: devbox run -- ./scripts/launch-ci-e2e.sh env: - MCLI_PUBLIC_API_KEY: ${{ secrets.ATLAS_PUBLIC_KEY }} - MCLI_PRIVATE_API_KEY: ${{ secrets.ATLAS_PRIVATE_KEY }} - MCLI_ORG_ID: ${{ secrets.ATLAS_ORG_ID}} - MCLI_OPS_MANAGER_URL: "https://cloud-qa.mongodb.com/" - IMAGE_URL: "${{ env.GHCR_REPO }}:${{ needs.prepare-e2e.outputs.tag }}" - IMAGE_PULL_SECRET_REGISTRY: ghcr.io - IMAGE_PULL_SECRET_USERNAME: $ - IMAGE_PULL_SECRET_PASSWORD: "${{ secrets.GITHUB_TOKEN }}" - BUNDLE_IMAGE: "${{ env.GHCR_BUNDLES_REPO}}:${{ needs.prepare-e2e.outputs.tag }}" - K8S_PLATFORM: "${{ steps.properties.outputs.k8s_platform }}" - K8S_VERSION: "${{ steps.properties.outputs.k8s_version }}" - TEST_NAME: "${{ matrix.test }}" + TEST_NAME: ${{ matrix.test }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_ACCOUNT_ARN_LIST: ${{ secrets.AWS_ACCOUNT_ARN_LIST }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} @@ -262,8 +128,8 @@ jobs: GCP_SA_CRED: ${{ secrets.GCP_SA_CRED }} DATADOG_KEY: ${{ secrets.DATADOG_KEY }} PAGER_DUTY_SERVICE_KEY: ${{ secrets.PAGER_DUTY_SERVICE_KEY }} - run: devbox run -- ./scripts/launch-ci-e2e.sh - - name: Upload operator logs + + - name: Upload logs on failure if: ${{ failure() }} uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c66a83b19c..d824a17fe4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,12 +2,7 @@ name: Test on: schedule: - - cron: '0 0 * * 1-5' # Run a nightly test - push: - branches: - - 'main' - paths-ignore: - - 'docs/**' + - cron: '*/15 * * * *' pull_request: types: [opened, synchronize, reopened, ready_for_review, converted_to_draft, labeled, unlabeled] branches: @@ -34,32 +29,6 @@ jobs: steps: - name: allowed message run: echo "Allowed to run tests" - - lint: - needs: - - run-tests - uses: ./.github/workflows/lint.yaml - - validate-manifests: - needs: - - run-tests - uses: ./.github/workflows/validate-manifests.yml - - unit-tests: - needs: - - run-tests - uses: ./.github/workflows/test-unit.yml - secrets: inherit - - license-header-check: - needs: - - run-tests - uses: ./.github/workflows/license-header-check.yml - - check-licenses: - needs: - - run-tests - uses: ./.github/workflows/check-licenses.yml cloud-tests-filter: if: github.event.pull_request.head.repo.fork == false @@ -67,22 +36,8 @@ jobs: - run-tests uses: ./.github/workflows/cloud-tests-filter.yml - e2e2: - needs: - - lint - - unit-tests - - validate-manifests - - check-licenses - - cloud-tests-filter - uses: ./.github/workflows/tests-e2e2.yaml - secrets: inherit - cloud-tests: needs: - - lint - - unit-tests - - validate-manifests - - check-licenses - cloud-tests-filter if: | github.event_name == 'merge_group' || needs.cloud-tests-filter.outputs.run-cloud-tests == 'true' diff --git a/scripts/move-image.sh b/scripts/move-image.sh new file mode 100755 index 0000000000..d1f8f9c855 --- /dev/null +++ b/scripts/move-image.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# Copyright 2025 MongoDB Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# You may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script moves a multi-arch image from one registry to another using docker buildx. +set -euo pipefail + +# Required env vars +: "${IMAGE_SRC_REPO:?Missing IMAGE_SRC_REPO}" +: "${IMAGE_SRC_TAG:?Missing IMAGE_SRC_TAG}" +: "${IMAGE_DEST_REPO:?Missing IMAGE_DEST_REPO}" +: "${IMAGE_DEST_TAG:?Missing IMAGE_DEST_TAG}" + +# Optional env vars -> ALIAS TAG can be updated to a list later on +ALIAS_TAG="${ALIAS_TAG:-}" +ALIAS_ENABLED="${ALIAS_ENABLED:-false}" + +image_src_url="${IMAGE_SRC_REPO}:${IMAGE_SRC_TAG}" +image_dest_url="${IMAGE_DEST_REPO}:${IMAGE_DEST_TAG}" + +echo "Checking if ${image_dest_url} already exists remotely..." +if docker manifest inspect "${image_dest_url}" > /dev/null 2>&1; then + echo "Image ${image_dest_url} already exists. Skipping transfer." + exit 0 +fi + +echo "Transferring multi-arch image:" +echo " From: ${image_src_url}" +echo " To: ${image_dest_url}" + +BUILDER_NAME="tmpbuilder-move-image" +docker buildx create --name "${BUILDER_NAME}" --use > /dev/null +docker buildx imagetools create "${image_src_url}" --tag "${image_dest_url}" + +if [[ "${ALIAS_ENABLED}" == "true" && -n "${ALIAS_TAG}" ]]; then + echo "Aliasing ${image_src_url} as ${IMAGE_DEST_REPO}:${ALIAS_TAG}" + docker buildx imagetools create "${image_src_url}" --tag "${IMAGE_DEST_REPO}:${ALIAS_TAG}" + echo "Successfully aliased as ${IMAGE_DEST_REPO}:${ALIAS_TAG}" +fi + +docker buildx rm "${BUILDER_NAME}" > /dev/null +echo "Successfully moved ${image_src_url} -> ${image_dest_url}" \ No newline at end of file