From 8e531b38f7e47b1b30d2d7c71fc248a2c995b30a Mon Sep 17 00:00:00 2001 From: Paul Abel <128620221+pdabelf5@users.noreply.github.com> Date: Wed, 19 Jun 2024 09:12:28 +0100 Subject: [PATCH] Workflow refactor (#5766) --- .github/scripts/copy-images.sh | 2 +- .github/scripts/variables.sh | 2 +- .github/workflows/build-oss.yml | 207 +++---- .github/workflows/build-plus.yml | 215 +++----- .github/workflows/cache-update.yml | 62 +-- .github/workflows/ci.yml | 612 ++++++++++----------- .github/workflows/create-release-tag.yml | 56 -- .github/workflows/image-promotion.yml | 328 +++++++++++ .github/workflows/notifications.yml | 1 + .github/workflows/release.yml | 1 + .github/workflows/update-docker-sha.yml | 2 +- .github/workflows/updates-notification.yml | 5 +- build/Dockerfile | 65 ++- tests/Dockerfile | 2 +- 14 files changed, 846 insertions(+), 714 deletions(-) delete mode 100644 .github/workflows/create-release-tag.yml create mode 100644 .github/workflows/image-promotion.yml diff --git a/.github/scripts/copy-images.sh b/.github/scripts/copy-images.sh index d5eaba3cc5..dbff09ac56 100755 --- a/.github/scripts/copy-images.sh +++ b/.github/scripts/copy-images.sh @@ -42,7 +42,7 @@ SOURCE_NAP_WAF_DOS_IMAGE_PREFIX=${SOURCE_NAP_WAF_DOS_IMAGE_PREFIX:-"nginx-ic-dos TARGET_PLUS_IMAGE_PREFIX=${TARGET_PLUS_IMAGE_PREFIX:-"nginx-ic/nginx-plus-ingress"} TARGET_NAP_WAF_IMAGE_PREFIX=${TARGET_NAP_WAF_IMAGE_PREFIX:-"nginx-ic-nap/nginx-plus-ingress"} -TARGET_NAP_WAFV5_IMAGE_PREFIX=${TARGET_NAP_WAFV5_IMAGE_PREFIX:-"nginx-ic-nap/nginx-plus-ingress"} +TARGET_NAP_WAFV5_IMAGE_PREFIX=${TARGET_NAP_WAFV5_IMAGE_PREFIX:-"nginx-ic-nap-v5/nginx-plus-ingress"} TARGET_NAP_DOS_IMAGE_PREFIX=${TARGET_NAP_DOS_IMAGE_PREFIX:-"nginx-ic-dos/nginx-plus-ingress"} TARGET_NAP_WAF_DOS_IMAGE_PREFIX=${TARGET_NAP_WAF_DOS_IMAGE_PREFIX:-"nginx-ic-dos-nap/nginx-plus-ingress"} diff --git a/.github/scripts/variables.sh b/.github/scripts/variables.sh index 42b60a8b31..726ed04d8b 100755 --- a/.github/scripts/variables.sh +++ b/.github/scripts/variables.sh @@ -30,7 +30,7 @@ get_chart_md5() { } get_actions_md5() { - find .github .github/data/version.txt -type f -exec md5sum {} + | LC_ALL=C sort | md5sum | awk '{ print $1 }' + find .github -type f -exec md5sum {} + | LC_ALL=C sort | md5sum | awk '{ print $1 }' } get_build_tag() { diff --git a/.github/workflows/build-oss.yml b/.github/workflows/build-oss.yml index 69f7bc9ae8..24f8103a10 100644 --- a/.github/workflows/build-oss.yml +++ b/.github/workflows/build-oss.yml @@ -9,21 +9,29 @@ on: image: required: true type: string + tag: + required: false + type: string go-md5: required: true type: string base-image-md5: - required: false + required: true type: string - tag: - required: false + branch: + required: true type: string - publish-image: - required: false + authenticated: + required: true type: boolean - forked-workflow: + full-build: + description: Always build base image required: false type: boolean + default: false + ic-version: + required: false + type: string defaults: run: @@ -38,69 +46,23 @@ jobs: permissions: contents: read # for docker/build-push-action to read repo content security-events: write # for github/codeql-action/upload-sarif to upload SARIF results - id-token: write # for OIDC login to AWS ECR + id-token: write # for OIDC login to GCR packages: write # for docker/build-push-action to push to GHCR - outputs: - version: ${{ steps.meta.outputs.version }} - image_digest: ${{ steps.build-push.outputs.digest }} steps: - name: Checkout Repository uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: - ref: ${{ inputs.tag != '' && format('refs/tags/v{0}', inputs.tag) || github.ref }} + ref: ${{ inputs.branch }} fetch-depth: 0 - - name: Fetch Cached Artifacts - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 - with: - path: ${{ github.workspace }}/dist - key: nginx-ingress-${{ inputs.go-md5 }} - - name: Setup QEMU uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 with: platforms: arm,arm64,ppc64le,s390x - if: ${{ inputs.publish-image }} - name: Docker Buildx uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 - - name: DockerHub Login - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - if: ${{ inputs.publish-image }} - - - name: Login to GitHub Container Registry - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - if: ${{ inputs.publish-image }} - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2 - with: - aws-region: us-east-1 - role-to-assume: ${{ secrets.AWS_ROLE_PUBLIC_ECR }} - if: ${{ inputs.publish-image }} - - - name: Login to Public ECR - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 - with: - registry: public.ecr.aws - if: ${{ inputs.publish-image }} - - - name: Login to Quay.io - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 - with: - registry: quay.io - username: ${{ secrets.QUAY_USERNAME }} - password: ${{ secrets.QUAY_ROBOT_TOKEN }} - if: ${{ inputs.publish-image }} - - name: Authenticate to Google Cloud id: auth uses: google-github-actions/auth@71fee32a0bb7e97b4d33d548e7d957010649d8fa # v2.1.3 @@ -108,7 +70,7 @@ jobs: token_format: access_token workload_identity_provider: ${{ secrets.GCR_WORKLOAD_IDENTITY }} service_account: ${{ secrets.GCR_SERVICE_ACCOUNT }} - if: ${{ ! inputs.forked-workflow }} + if: ${{ inputs.authenticated }} - name: Login to GCR uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 @@ -116,7 +78,28 @@ jobs: registry: gcr.io username: oauth2accesstoken password: ${{ steps.auth.outputs.access_token }} - if: ${{ ! inputs.forked-workflow }} + if: ${{ inputs.authenticated }} + + - name: Docker meta + id: meta + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 + with: + context: workflow + images: | + name=gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/nginx-ic/nginx-ingress + flavor: | + suffix=${{ contains(inputs.image, 'ubi') && '-ubi' || '' }}${{ contains(inputs.image, 'alpine') && '-alpine' || '' }} + tags: | + type=raw,value=${{ inputs.tag }} + labels: | + org.opencontainers.image.description=NGINX Ingress Controller for Kubernetes + io.artifacthub.package.readme-url=https://raw.githubusercontent.com/nginxinc/kubernetes-ingress/main/README.md + io.artifacthub.package.logo-url=https://docs.nginx.com/nginx-ingress-controller/images/icons/NGINX-Ingress-Controller-product-icon.svg + io.artifacthub.package.maintainers=[{"name":"NGINX Inc","email":"kubernetes@nginx.com"}] + io.artifacthub.package.license=Apache-2.0 + io.artifacthub.package.keywords=kubernetes,ingress,nginx,controller + env: + DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index - name: Check if base images exist id: base_exists @@ -126,64 +109,48 @@ jobs: if docker manifest inspect ${base_image}; then echo "exists=true" >> $GITHUB_OUTPUT fi - if: ${{ ! inputs.forked-workflow }} + if: ${{ inputs.authenticated && ! inputs.full-build }} - name: Build Base Container uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 with: file: build/Dockerfile context: "." - cache-from: type=gha,scope=${{ inputs.image }} cache-to: type=gha,scope=${{ inputs.image }},mode=max target: common tags: ${{ steps.base_exists.outputs.image }} platforms: ${{ inputs.platforms }} pull: true push: true + no-cache: true build-args: | BUILD_OS=${{ inputs.image }} - IC_VERSION=${{ inputs.tag }} - if: ${{ ! inputs.forked-workflow && steps.base_exists.outputs.exists != 'true' }} + IC_VERSION=${{ inputs.ic-version && inputs.ic-version || steps.meta.outputs.version }} + if: ${{ inputs.authenticated && steps.base_exists.outputs.exists != 'true' }} + + - name: Check if target image exists + id: target_exists + run: | + if docker pull ${{ steps.meta.outputs.tags }}; then + echo "exists=true" >> $GITHUB_OUTPUT + fi + if: ${{ inputs.authenticated && ! inputs.full-build }} - - name: Get short tag - id: tag + - name: Debug values run: | - version="${{ inputs.tag }}" - short="${version%.*}" - echo "short=$short" >> $GITHUB_OUTPUT - if: ${{ inputs.tag != '' }} + echo "authenticated: ${{ inputs.authenticated }}" + echo "base_exists: ${{ steps.base_exists.outputs.exists }}" + echo "target_exists: ${{ steps.target_exists.outputs.exists }}" + echo "full-build: ${{ inputs.full-build }}" + echo "all: ${{ inputs.authenticated || steps.base_exists.outputs.exists != 'true' || steps.target_exists.outputs.exists != 'true' }}" - - name: Docker meta - id: meta - uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 + - name: Fetch Cached Artifacts + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: - context: ${{ inputs.tag != '' && 'git' || 'workflow' }} - images: | - name=nginx/nginx-ingress - name=ghcr.io/nginxinc/kubernetes-ingress - name=public.ecr.aws/nginx/nginx-ingress - name=quay.io/nginx/nginx-ingress - flavor: | - latest=${{ (inputs.tag != '' && 'true') || 'auto' }} - suffix=${{ contains(inputs.image, 'ubi') && '-ubi' || '' }}${{ contains(inputs.image, 'alpine') && '-alpine' || '' }},onlatest=true - tags: | - type=edge - type=ref,event=pr - type=ref,event=branch,enable=${{ startsWith(github.ref, 'refs/heads/release-') }} - type=schedule,enable=${{ inputs.tag == '' }} - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=raw,value=${{ inputs.tag }},enable=${{ inputs.tag != '' }} - type=raw,value=${{ steps.tag.outputs.short }},enable=${{ inputs.tag != '' }} - labels: | - org.opencontainers.image.description=NGINX Ingress Controller for Kubernetes - io.artifacthub.package.readme-url=https://raw.githubusercontent.com/nginxinc/kubernetes-ingress/main/README.md - io.artifacthub.package.logo-url=https://docs.nginx.com/nginx-ingress-controller/images/icons/NGINX-Ingress-Controller-product-icon.svg - io.artifacthub.package.maintainers=[{"name":"NGINX Inc","email":"kubernetes@nginx.com"}] - io.artifacthub.package.license=Apache-2.0 - io.artifacthub.package.keywords=kubernetes,ingress,nginx,controller - env: - DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index + path: ${{ github.workspace }}/dist + key: nginx-ingress-${{ inputs.go-md5 }} + fail-on-cache-miss: true + if: ${{ inputs.authenticated || steps.base_exists.outputs.exists != 'true' || steps.target_exists.outputs.exists != 'true' }} - name: Build Docker image uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 @@ -193,35 +160,21 @@ jobs: context: "." cache-from: type=gha,scope=${{ inputs.image }} cache-to: type=gha,scope=${{ inputs.image }},mode=max - target: goreleaser${{ ! inputs.forked-workflow && '-prebuilt' || '' }} + target: goreleaser${{ inputs.authenticated && '-prebuilt' || '' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - annotations: ${{ github.event_name != 'pull_request' && steps.meta.outputs.annotations || '' }} - platforms: ${{ github.event_name != 'pull_request' && ! startsWith(github.ref, 'refs/heads/release-') && inputs.platforms || '' }} - load: ${{ github.event_name == 'pull_request' || startsWith(github.ref, 'refs/heads/release-') }} - push: ${{ inputs.publish-image }} + annotations: ${{ steps.meta.outputs.annotations }} + platforms: ${{ inputs.platforms }} + load: false + push: ${{ inputs.authenticated }} pull: true - no-cache: ${{ inputs.publish-image }} - sbom: ${{ inputs.publish-image }} + sbom: ${{ inputs.authenticated }} provenance: false build-args: | BUILD_OS=${{ inputs.image }} - ${{ ! inputs.forked-workflow && format('PREBUILT_BASE_IMG={0}', steps.base_exists.outputs.image) || '' }} - IC_VERSION=${{ (github.event_name == 'pull_request' || startsWith(github.ref, 'refs/heads/release-')) && 'CI' || steps.meta.outputs.version }} - - - name: Certify Images - continue-on-error: true - run: | - curl -fsSL https://github.com/redhat-openshift-ecosystem/openshift-preflight/releases/download/1.6.11/preflight-linux-amd64 --output preflight - chmod +x preflight - - IFS=',' read -ra arch_list <<< "${{ inputs.platforms }}" - - for arch in "${arch_list[@]}"; do - architecture=("${arch#*/}") - ./preflight check container quay.io/nginx/nginx-ingress:${{ steps.meta.outputs.version }} --pyxis-api-token ${{ secrets.PYXIS_API_TOKEN }} --certification-project-id ${{ secrets.CERTIFICATION_PROJECT_ID }} --platform $architecture --submit - done - if: ${{ (github.ref_type == 'tag' && vars.OLD_RELEASE_FLOW == 'true') && contains(inputs.image, 'ubi') }} + ${{ inputs.authenticated && format('PREBUILT_BASE_IMG={0}', steps.base_exists.outputs.image) }} + IC_VERSION=${{ inputs.ic-version && inputs.ic-version || steps.meta.outputs.version }} + if: ${{ steps.base_exists.outputs.exists != 'true' || steps.target_exists.outputs.exists != 'true' }} - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@595be6a0f6560a0a8fc419ddf630567fc623531d # 0.22.0 @@ -231,12 +184,14 @@ jobs: format: "sarif" output: "trivy-results-${{ inputs.image }}.sarif" ignore-unfixed: "true" + if: ${{ inputs.authenticated && steps.build-push.conclusion == 'success' }} - name: Upload Trivy scan results to GitHub Security tab uses: github/codeql-action/upload-sarif@530d4feaa9c62aaab2d250371e2061eb7a172363 # v3.25.9 continue-on-error: true with: sarif_file: "trivy-results-${{ inputs.image }}.sarif" + if: ${{ inputs.authenticated && steps.build-push.conclusion == 'success' }} - name: Upload Scan Results uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 @@ -245,17 +200,3 @@ jobs: name: "trivy-results-${{ inputs.image }}.sarif" path: "trivy-results-${{ inputs.image }}.sarif" if: always() - - send-notification: - name: Send Notification - needs: build - uses: ./.github/workflows/updates-notification.yml - with: - tag: ${{ inputs.tag }} - version: ${{ needs.build.outputs.version }} - image_digest: ${{ needs.build.outputs.image_digest }} - permissions: - contents: read - actions: read - secrets: inherit - if: ${{ inputs.tag != '' }} diff --git a/.github/workflows/build-plus.yml b/.github/workflows/build-plus.yml index d7c7358368..c70de87006 100644 --- a/.github/workflows/build-plus.yml +++ b/.github/workflows/build-plus.yml @@ -9,8 +9,8 @@ on: image: required: true type: string - target: - required: true + tag: + required: false type: string go-md5: required: true @@ -18,24 +18,26 @@ on: base-image-md5: required: false type: string - nap_modules: + branch: required: false type: string - release-url: + nap-modules: required: false type: string - publish-image: - required: false - type: boolean - publish-aws-market-place: - required: false + target: + required: true + type: string + authenticated: + required: true type: boolean - publish-nginx-reqistry: + full-build: + description: Always build base image required: false type: boolean - forked-workflow: + default: false + ic-version: required: false - type: boolean + type: string defaults: run: @@ -50,24 +52,18 @@ jobs: contents: read # for docker/build-push-action to read repo content security-events: write # for github/codeql-action/upload-sarif to upload SARIF results id-token: write # for OIDC login to AWS - runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-22.04' || 'kic-plus' }} + runs-on: ubuntu-22.04 steps: - name: Checkout Repository uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: + ref: ${{ inputs.branch }} fetch-depth: 0 - - name: Fetch Cached Artifacts - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 - with: - path: ${{ github.workspace }}/dist - key: nginx-ingress-${{ inputs.go-md5 }} - - name: Setup QEMU uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 with: platforms: arm64,s390x - if: ${{ inputs.publish-image }} - name: Docker Buildx uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 @@ -79,7 +75,7 @@ jobs: token_format: access_token workload_identity_provider: ${{ secrets.GCR_WORKLOAD_IDENTITY }} service_account: ${{ secrets.GCR_SERVICE_ACCOUNT }} - if: ${{ inputs.publish-image || ! inputs.forked-workflow }} + if: ${{ inputs.authenticated }} - name: Login to GCR uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 @@ -87,172 +83,125 @@ jobs: registry: gcr.io username: oauth2accesstoken password: ${{ steps.auth.outputs.access_token }} - if: ${{ inputs.publish-image || ! inputs.forked-workflow }} - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2 - with: - aws-region: us-east-1 - role-to-assume: ${{ secrets.AWS_ROLE_MARKETPLACE }} - if: ${{ inputs.publish-aws-market-place }} - - name: Login to ECR - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 - with: - registry: 709825985650.dkr.ecr.us-east-1.amazonaws.com - if: ${{ inputs.publish-aws-market-place }} + if: ${{ inputs.authenticated }} - - name: Get Id Token - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - id: idtoken - with: - script: | - let id_token = await core.getIDToken() - core.setOutput('id_token', id_token) - if: ${{ inputs.publish-nginx-reqistry }} - - - name: Login to NGINX Registry - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 - with: - registry: docker-mgmt.nginx.com - username: ${{ steps.idtoken.outputs.id_token }} - password: ${{ github.actor }} - if: ${{ inputs.publish-nginx-reqistry }} + - name: NAP modules + id: nap_modules + run: | + [[ "${{ inputs.nap-modules }}" == "waf,dos" ]] && modules="waf-dos" || name="${{ inputs.nap-modules }}" + [[ "${{ inputs.nap-modules }}" == "waf,dos" ]] && modules="both" || modules="${{ inputs.nap-modules }}" + echo "modules=${modules}" >> $GITHUB_OUTPUT + echo "name=${name}" >> $GITHUB_OUTPUT + if: ${{ inputs.nap-modules != '' }} - name: Docker meta id: meta uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 with: images: | - name=gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/nginx-ic${{ contains(inputs.nap_modules, 'dos') && '-dos' || '' }}${{ contains(inputs.nap_modules, 'waf') && '-nap' || '' }}${{ contains(inputs.image, 'v5') && '-v5' || '' }}/nginx-plus-ingress - name=docker-mgmt.nginx.com/nginx-ic${{ contains(inputs.nap_modules, 'waf') && '-nap' || '' }}${{ contains(inputs.image, 'v5') && '-v5' || '' }}${{ contains(inputs.nap_modules, 'dos') && '-dos' || '' }}/nginx-plus-ingress,enable=${{ inputs.publish-nginx-reqistry && ! contains(inputs.target, 'aws') }} - name=709825985650.dkr.ecr.us-east-1.amazonaws.com/nginx/nginx-plus-ingress${{ contains(inputs.nap_modules, 'dos') && '-dos' || '' }}${{ contains(inputs.nap_modules, 'waf') && '-nap' || '' }},enable=${{ inputs.publish-aws-market-place && contains(inputs.target, 'aws') }} + name=gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/nginx-ic${{ contains(inputs.nap-modules, 'dos') && '-dos' || '' }}${{ contains(inputs.nap-modules, 'waf') && '-nap' || '' }}${{ contains(inputs.image, 'v5') && '-v5' || '' }}/nginx-plus-ingress flavor: | - suffix=${{ contains(inputs.image, 'ubi') && '-ubi' || '' }}${{ contains(inputs.image, 'alpine') && '-alpine' || '' }}${{ contains(inputs.target, 'aws') && '-mktpl' || '' }}${{ contains(inputs.image, 'fips') && '-fips' || ''}},onlatest=true - latest=${{ contains(inputs.target, 'aws') && 'false' || 'auto' }} + suffix=${{ contains(inputs.image, 'ubi') && '-ubi' || '' }}${{ contains(inputs.image, 'alpine') && '-alpine' || '' }}${{ contains(inputs.target, 'aws') && '-mktpl' || '' }}${{ contains(inputs.image, 'fips') && '-fips' || ''}} tags: | - type=edge - type=ref,event=pr - type=ref,event=branch,enable=${{ startsWith(github.ref, 'refs/heads/release-') }} - type=schedule - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} + type=raw,value=${{ inputs.tag }} labels: | org.opencontainers.image.description=NGINX Plus Ingress Controller for Kubernetes env: DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index - - name: NAP modules - id: nap_modules - run: | - [[ "${{ inputs.nap_modules }}" == "waf,dos" ]] && modules="waf-dos" || name="${{ inputs.nap_modules }}" - [[ "${{ inputs.nap_modules }}" == "waf,dos" ]] && modules="both" || modules="${{ inputs.nap_modules }}" - echo "modules=${modules}" >> $GITHUB_OUTPUT - echo "name=${name}" >> $GITHUB_OUTPUT - if: ${{ inputs.nap_modules != '' }} - - name: Check if base images exist id: base_exists run: | base_image="gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/nginx-ic-base/plus:${{ inputs.base-image-md5 }}-${{ inputs.image }}${{ steps.nap_modules.outputs.name != '' && format('-{0}', steps.nap_modules.outputs.name) || '' }}${{ contains(inputs.image, 'v5') && '-v5' || '' }}" echo "image=${base_image}" >> $GITHUB_OUTPUT - if docker manifest inspect ${base_image}; then + if docker pull ${base_image}; then echo "exists=true" >> $GITHUB_OUTPUT fi - if: ${{ ! inputs.forked-workflow }} + if: ${{ inputs.authenticated && ! inputs.full-build }} - name: Build Base Container uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 with: file: build/Dockerfile context: "." - cache-from: type=gha,scope=${{ inputs.image }}${{ steps.nap_modules.outputs.name != '' && format('-{0}', steps.nap_modules.outputs.name) || '' }} cache-to: type=gha,scope=${{ inputs.image }}${{ steps.nap_modules.outputs.name != '' && format('-{0}', steps.nap_modules.outputs.name) || '' }},mode=max target: common tags: ${{ steps.base_exists.outputs.image }} platforms: ${{ inputs.platforms }} pull: true push: true + no-cache: true build-args: | BUILD_OS=${{ inputs.image }} - IC_VERSION=${{ (github.ref_type == 'tag' && vars.OLD_RELEASE_FLOW == 'true') && steps.meta.outputs.version || 'CI' }} - ${{ steps.nap_modules.outputs.modules != '' && format('NAP_MODULES={0}', steps.nap_modules.outputs.modules) || '' }} + IC_VERSION=${{ inputs.ic-version && inputs.ic-version || steps.meta.outputs.version }} + ${{ inputs.nap-modules != '' && format('NAP_MODULES={0}', steps.nap_modules.outputs.name) || '' }} secrets: | - "nginx-repo.crt=${{ inputs.nap_modules != '' && secrets.NGINX_AP_CRT || secrets.NGINX_CRT }}" - "nginx-repo.key=${{ inputs.nap_modules != '' && secrets.NGINX_AP_KEY || secrets.NGINX_KEY }}" - ${{ inputs.nap_modules != '' && contains(inputs.image, 'ubi') && format('"rhel_license={0}"', secrets.RHEL_LICENSE) || '' }} - if: ${{ ! inputs.forked-workflow && steps.base_exists.outputs.exists != 'true' }} + "nginx-repo.crt=${{ inputs.nap-modules != '' && secrets.NGINX_AP_CRT || secrets.NGINX_CRT }}" + "nginx-repo.key=${{ inputs.nap-modules != '' && secrets.NGINX_AP_KEY || secrets.NGINX_KEY }}" + ${{ inputs.nap-modules != '' && contains(inputs.image, 'ubi') && format('"rhel_license={0}"', secrets.RHEL_LICENSE) || '' }} + if: ${{ inputs.authenticated && steps.base_exists.outputs.exists != 'true' }} - - name: Build Plus Docker image - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 + - name: Check if target image exists + id: target_exists + run: | + if docker pull ${{ steps.meta.outputs.tags }}; then + echo "exists=true" >> $GITHUB_OUTPUT + fi + if: ${{ inputs.authenticated && ! inputs.full-build }} + + - name: Debug values + run: | + echo "authenticated: ${{ inputs.authenticated }}" + echo "base_exists: ${{ steps.base_exists.outputs.exists }}" + echo "target_exists: ${{ steps.target_exists.outputs.exists }}" + echo "full-build: ${{ inputs.full-build }}" + echo "all: ${{ inputs.authenticated || steps.base_exists.outputs.exists != 'true' || steps.target_exists.outputs.exists != 'true' }}" + + - name: Fetch Cached Artifacts + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + with: + path: ${{ github.workspace }}/dist + key: nginx-ingress-${{ inputs.go-md5 }} + fail-on-cache-miss: true + if: ${{ inputs.authenticated || steps.base_exists.outputs.exists != 'true' || steps.target_exists.outputs.exists != 'true' }} + + - name: Build Docker image + uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0 + id: build-push with: file: build/Dockerfile context: "." cache-from: type=gha,scope=${{ inputs.image }}${{ steps.nap_modules.outputs.name != '' && format('-{0}', steps.nap_modules.outputs.name) || '' }} cache-to: type=gha,scope=${{ inputs.image }}${{ steps.nap_modules.outputs.name != '' && format('-{0}', steps.nap_modules.outputs.name) || '' }},mode=max - target: ${{ inputs.target }}${{ ! inputs.forked-workflow && '-prebuilt' || '' }} + target: ${{ inputs.target }}${{ inputs.authenticated && '-prebuilt' || '' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - annotations: ${{ inputs.publish-image && steps.meta.outputs.annotations || '' }} - platforms: ${{ inputs.publish-image && inputs.platforms || '' }} - load: ${{ ! inputs.publish-image }} - push: ${{ inputs.publish-image }} + annotations: ${{ steps.meta.outputs.annotations }} + platforms: ${{ inputs.platforms }} + load: false + push: ${{ inputs.authenticated }} pull: true - no-cache: ${{ inputs.publish-image }} - sbom: ${{ inputs.publish-image }} + sbom: ${{ inputs.authenticated }} provenance: false build-args: | BUILD_OS=${{ inputs.image }} - ${{ ! inputs.forked-workflow && format('PREBUILT_BASE_IMG={0}', steps.base_exists.outputs.image) || '' }} - IC_VERSION=${{ (github.ref_type == 'tag' && vars.OLD_RELEASE_FLOW == 'true') && steps.meta.outputs.version || 'CI' }} - ${{ inputs.nap_modules != '' && format('NAP_MODULES={0}', steps.nap_modules.outputs.modules) || '' }} + ${{ inputs.authenticated && format('PREBUILT_BASE_IMG={0}', steps.base_exists.outputs.image ) }} + IC_VERSION=${{ inputs.ic-version && inputs.ic-version || steps.meta.outputs.version }} + ${{ inputs.nap-modules != '' && format('NAP_MODULES={0}', steps.nap_modules.outputs.name) || '' }} + ${{ (contains(inputs.target, 'aws') && inputs.nap-modules != '') && format('NAP_MODULES_AWS={0}', steps.nap_modules.outputs.modules) || '' }} ${{ contains(inputs.image, 'v5') && 'WAF_VERSION=v5' || '' }} - ${{ (contains(inputs.target, 'aws') && inputs.nap_modules != '') && format('NAP_MODULES_AWS={0}', steps.nap_modules.outputs.modules) || '' }} secrets: | - "nginx-repo.crt=${{ inputs.nap_modules != '' && secrets.NGINX_AP_CRT || secrets.NGINX_CRT }}" - "nginx-repo.key=${{ inputs.nap_modules != '' && secrets.NGINX_AP_KEY || secrets.NGINX_KEY }}" - ${{ inputs.nap_modules != '' && contains(inputs.image, 'ubi') && format('"rhel_license={0}"', secrets.RHEL_LICENSE) || '' }} - - - name: AWS variables - id: aws - run: | - aws_registry=$(echo "${{ steps.meta.outputs.tags }}" | grep -oP "709825985650.dkr.ecr.us-east-1.amazonaws.com/[^[:space:]]+:${{ steps.meta.outputs.version }}") - version=$(echo ${{ steps.meta.outputs.version }} | sed 's/-mktpl//') - declare -A nap_mapping=( - ["waf"]=_NAP_WAF - ["dos"]=_NAP_DOS - ["waf,dos"]=_NAP_WAF_DOS - ) - modules=${{ inputs.nap_modules }} - [[ -n $modules && ${nap_mapping[$modules]+_} ]] && nap=${nap_mapping[$modules]} - - echo "version=$version" >> $GITHUB_OUTPUT - echo "product_code=AWS${nap}_PRODUCT_ID" >> $GITHUB_OUTPUT - echo "registry=${aws_registry}" >> $GITHUB_OUTPUT - if: ${{ inputs.publish-aws-market-place }} - - - name: Publish to AWS Marketplace - uses: nginxinc/aws-marketplace-publish@be512a7ae9666098bc4429a1afa27a11be6a3995 # v1.0.3 - continue-on-error: true - with: - version: ${{ steps.aws.outputs.version }} - product-id: ${{ secrets[steps.aws.outputs.product_code] }} - registry: ${{ steps.aws.outputs.registry }} - release-notes: ${{ inputs.release-url }} - description: | - Best-in-class traffic management solution for services in Amazon EKS. - This is the official implementation of NGINX Ingress Controller (based on NGINX Plus) from NGINX. - usage-instructions: | - This container requires Kubernetes and can be deployed to EKS. - Review the installation instructions https://docs.nginx.com/nginx-ingress-controller/installation/ and utilize the deployment resources available https://github.com/nginxinc/kubernetes-ingress/tree/master/deployments - Use this image instead of building your own. - if: ${{ inputs.publish-aws-market-place }} + "nginx-repo.crt=${{ inputs.nap-modules != '' && secrets.NGINX_AP_CRT || secrets.NGINX_CRT }}" + "nginx-repo.key=${{ inputs.nap-modules != '' && secrets.NGINX_AP_KEY || secrets.NGINX_KEY }}" + ${{ contains(inputs.image, 'ubi') && format('"rhel_license={0}"', secrets.RHEL_LICENSE) || '' }} + if: ${{ steps.base_exists.outputs.exists != 'true' || steps.target_exists.outputs.exists != 'true' }} - name: Extract image name for Trivy id: trivy-tag run: | tag=$(echo $DOCKER_METADATA_OUTPUT_JSON | jq -r '[ .tags[] | select(contains("f5-gcs-7899"))] | .[0]') echo "tag=$tag" >> $GITHUB_OUTPUT - if: ${{ inputs.publish-image }} + if: ${{ inputs.authenticated && steps.build-push.conclusion == 'success' }} - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@595be6a0f6560a0a8fc419ddf630567fc623531d # 0.22.0 @@ -262,14 +211,14 @@ jobs: format: "sarif" output: "trivy-results-${{ inputs.image }}.sarif" ignore-unfixed: "true" - if: ${{ inputs.publish-image }} + if: ${{ inputs.authenticated && steps.build-push.conclusion == 'success' }} - name: Upload Trivy scan results to GitHub Security tab uses: github/codeql-action/upload-sarif@530d4feaa9c62aaab2d250371e2061eb7a172363 # v3.25.9 continue-on-error: true with: sarif_file: "trivy-results-${{ inputs.image }}.sarif" - if: ${{ inputs.publish-image }} + if: ${{ inputs.authenticated && steps.build-push.conclusion == 'success' }} - name: Upload Scan Results uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 diff --git a/.github/workflows/cache-update.yml b/.github/workflows/cache-update.yml index 77be39d20b..33fda78f6f 100644 --- a/.github/workflows/cache-update.yml +++ b/.github/workflows/cache-update.yml @@ -21,7 +21,6 @@ jobs: outputs: go_code_md5: ${{ steps.vars.outputs.go_code_md5 }} docker_md5: ${{ steps.vars.outputs.docker_md5 }} - chart_version: ${{ steps.vars.outputs.chart_version }} steps: - name: Checkout Repository uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 @@ -32,46 +31,8 @@ jobs: ./.github/scripts/variables.sh go_code_md5 >> $GITHUB_OUTPUT ./.github/scripts/variables.sh docker_md5 >> $GITHUB_OUTPUT source .github/data/version.txt - echo "chart_version=${HELM_CHART_VERSION}" >> $GITHUB_OUTPUT cat $GITHUB_OUTPUT - release-notes: - name: Release Notes - runs-on: ubuntu-22.04 - needs: checks - outputs: - release-url: ${{ steps.release-notes.outputs.release-url }} - permissions: - contents: write # for lucacome/draft-release - steps: - - name: Checkout Repository - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - with: - fetch-depth: 0 - - - name: Create/Update Draft - uses: lucacome/draft-release@8a63d32c79a171ae6048e614a8988f0ac3ed56d4 # v1.1.0 - id: release-notes - with: - minor-label: "enhancement" - major-label: "change" - publish: false - collapse-after: 50 - variables: | - helm-chart=${{ needs.checks.outputs.chart_version }} - notes-footer: | - ## Upgrade - - For NGINX, use the {{version}} images from our [DockerHub](https://hub.docker.com/r/nginx/nginx-ingress/tags?page=1&ordering=last_updated&name={{version-number}}), [GitHub Container](https://github.com/nginxinc/kubernetes-ingress/pkgs/container/kubernetes-ingress), [Amazon ECR Public Gallery](https://gallery.ecr.aws/nginx/nginx-ingress) or [Quay.io](https://quay.io/repository/nginx/nginx-ingress). - - For NGINX Plus, use the {{version}} images from the F5 Container registry, the [AWS Marketplace](https://aws.amazon.com/marketplace/search/?CREATOR=741df81b-dfdc-4d36-b8da-945ea66b522c&FULFILLMENT_OPTION_TYPE=CONTAINER&filters=CREATOR%2CFULFILLMENT_OPTION_TYPE), the [GCP Marketplace](https://console.cloud.google.com/marketplace/browse?filter=partner:F5,%20Inc.&filter=solution-type:k8s&filter=category:networking) or build your own image using the {{version}} source code. - - For Helm, use version {{helm-chart}} of the chart. - - ## Resources - - Documentation -- https://docs.nginx.com/nginx-ingress-controller/ - - Configuration examples -- https://github.com/nginxinc/kubernetes-ingress/tree/{{version}}/examples - - Helm Chart -- https://github.com/nginxinc/kubernetes-ingress/tree/{{version}}/charts/nginx-ingress - - Operator -- https://github.com/nginxinc/nginx-ingress-helm-operator - if: ${{ github.event_name == 'push' && github.ref != 'refs/heads/main' }} - build-docker: name: Build Docker OSS needs: [checks] @@ -90,8 +51,9 @@ jobs: image: ${{ matrix.image }} go-md5: ${{ needs.checks.outputs.go_code_md5 }} base-image-md5: ${{ needs.checks.outputs.docker_md5 }} - publish-image: false - forked-workflow: true + authenticated: false + tag: "edge" + branch: ${{ github.ref }} permissions: contents: read actions: read @@ -102,7 +64,7 @@ jobs: build-docker-plus: name: Build Docker Plus - needs: [checks, release-notes] + needs: [checks] strategy: fail-fast: false matrix: @@ -120,9 +82,9 @@ jobs: target: ${{ matrix.target }} go-md5: ${{ needs.checks.outputs.go_code_md5 }} base-image-md5: ${{ needs.checks.outputs.docker_md5 }} - release-url: ${{ needs.release-notes.outputs.release-url }} - publish-image: false - forked-workflow: true + authenticated: false + tag: "edge" + branch: ${{ github.ref }} permissions: contents: read security-events: write @@ -131,7 +93,7 @@ jobs: build-docker-nap: name: Build Docker NAP - needs: [checks, release-notes] + needs: [checks] strategy: fail-fast: false matrix: @@ -191,10 +153,10 @@ jobs: target: ${{ matrix.target }} go-md5: ${{ needs.checks.outputs.go_code_md5 }} base-image-md5: ${{ needs.checks.outputs.docker_md5 }} - nap_modules: ${{ matrix.nap_modules }} - release-url: ${{ needs.release-notes.outputs.release-url }} - publish-image: false - forked-workflow: true + nap-modules: ${{ matrix.nap_modules }} + authenticated: false + tag: "edge" + branch: ${{ github.ref }} permissions: contents: read security-events: write diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7852f381c7..fe123a6931 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,24 +1,17 @@ name: CI on: - push: - branches: - - main - - release-* - tags: - - "v[0-9]+.[0-9]+.[0-9]+" pull_request: branches: - main - release-* merge_group: - schedule: - - cron: "0 4 * * *" # run every day at 04:00 UTC workflow_dispatch: inputs: - publish-image: - description: Publish of the generated images + force: type: boolean + description: "Force rebuild" + required: false default: false defaults: @@ -36,6 +29,9 @@ jobs: checks: name: Checks and variables runs-on: ubuntu-22.04 + permissions: + contents: read + id-token: write outputs: docs_only: ${{ github.event.pull_request && steps.docs.outputs.docs_only == 'true' }} k8s_latest: ${{ steps.vars.outputs.k8s_latest }} @@ -45,11 +41,11 @@ jobs: binary_cache_hit: ${{ steps.binary-cache.outputs.cache-hit }} chart_version: ${{ steps.vars.outputs.chart_version }} ic_version: ${{ steps.vars.outputs.ic_version }} - publish_images: ${{ steps.vars.outputs.publish }} docker_md5: ${{ steps.vars.outputs.docker_md5 }} build_tag: ${{ steps.vars.outputs.build_tag }} stable_tag: ${{ steps.vars.outputs.stable_tag }} forked_workflow: ${{ steps.vars.outputs.forked_workflow }} + stable_image_exists: ${{ steps.stable_exists.outputs.exists }} steps: - name: Checkout Repository uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 @@ -104,15 +100,6 @@ jobs: echo "ic_version=${IC_VERSION}" >> $GITHUB_OUTPUT echo "chart_version=${HELM_CHART_VERSION}" >> $GITHUB_OUTPUT echo "forked_workflow=${{ (github.event.pull_request && github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) || github.repository != 'nginxinc/kubernetes-ingress' }}" >> $GITHUB_OUTPUT - publish=false - if ${{ github.event_name == 'workflow_dispatch' && inputs.publish-image }}; then - publish=true - elif ${{ (github.ref_type == 'tag' && vars.OLD_RELEASE_FLOW == 'true') && startsWith(github.ref, 'refs/heads/release-') }}; then - publish=true - elif ${{ github.event_name != 'workflow_dispatch' && github.event_name != 'pull_request' && ! startsWith(github.ref, 'refs/heads/release-') }}; then - publish=true - fi - echo "publish=$publish" >> $GITHUB_OUTPUT ./.github/scripts/variables.sh go_code_md5 >> $GITHUB_OUTPUT ./.github/scripts/variables.sh docker_md5 >> $GITHUB_OUTPUT ./.github/scripts/variables.sh build_tag >> $GITHUB_OUTPUT @@ -139,6 +126,47 @@ jobs: make update-codegen && git diff --name-only --exit-code pkg/** cd ../../.. && mv github.com/nginxinc/kubernetes-ingress kubernetes-ingress/kubernetes-ingress + - name: Authenticate to Google Cloud + id: auth + uses: google-github-actions/auth@55bd3a7c6e2ae7cf1877fd1ccb9d54c0503c457c # v2.1.2 + with: + token_format: access_token + workload_identity_provider: ${{ secrets.GCR_WORKLOAD_IDENTITY }} + service_account: ${{ secrets.GCR_SERVICE_ACCOUNT }} + if: ${{ steps.vars.outputs.forked_workflow == 'false' }} + + - name: Login to GCR + uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0 + with: + registry: gcr.io + username: oauth2accesstoken + password: ${{ steps.auth.outputs.access_token }} + if: ${{ steps.vars.outputs.forked_workflow == 'false' }} + + - name: Check if stable image exists + id: stable_exists + run: | + if docker pull gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/nginx-ic/nginx-ingress:${{ steps.vars.outputs.stable_tag }}; then + echo "exists=true" >> $GITHUB_OUTPUT + fi + if: ${{ steps.vars.outputs.forked_workflow == 'false' }} + + - name: Output variables + run: | + echo docs_only: ${{ github.event.pull_request && steps.docs.outputs.docs_only == 'true' }} + echo k8s_latest: ${{ steps.vars.outputs.k8s_latest }} + echo latest_kindest_node_versions: ${{ steps.vars.outputs.latest_kindest_node_versions }} + echo go_path: ${{ steps.vars.outputs.go_path }} + echo go_code_md5: ${{ steps.vars.outputs.go_code_md5 }} + echo binary_cache_hit: ${{ steps.binary-cache.outputs.cache-hit }} + echo chart_version: ${{ steps.vars.outputs.chart_version }} + echo ic_version: ${{ steps.vars.outputs.ic_version }} + echo docker_md5: ${{ steps.vars.outputs.docker_md5 }} + echo build_tag: ${{ steps.vars.outputs.build_tag }} + echo stable_tag: ${{ steps.vars.outputs.stable_tag }} + echo forked_workflow: ${{ steps.vars.outputs.forked_workflow }} + echo stable_image_exists: ${{ steps.stable_exists.outputs.exists }} + unit-tests: name: Unit Tests runs-on: ubuntu-22.04 @@ -146,14 +174,17 @@ jobs: steps: - name: Checkout Repository uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - name: Setup Golang Environment uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: go-version-file: go.mod if: ${{ needs.checks.outputs.binary_cache_hit != 'true' }} + - name: Run Tests run: make cover if: ${{ needs.checks.outputs.binary_cache_hit != 'true' }} + - name: Upload coverage to Codecov uses: codecov/codecov-action@125fc84a9a348dbcf27191600683ec096ec9021c # v4.4.1 with: @@ -218,19 +249,11 @@ jobs: go-version-file: go.mod if: ${{ needs.checks.outputs.binary_cache_hit != 'true' }} - - name: Download Syft - uses: anchore/sbom-action/download-syft@e8d2a6937ecead383dfe75190d104edd1f9c5751 # v0.16.0 - if: ${{ github.ref_type == 'tag' && vars.OLD_RELEASE_FLOW == 'true' }} - - - name: Install Cosign - uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0 - if: ${{ github.ref_type == 'tag' && vars.OLD_RELEASE_FLOW == 'true' }} - - name: Build binaries uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200 # v6.0.0 with: version: latest - args: ${{ (github.ref_type == 'tag' && vars.OLD_RELEASE_FLOW == 'true') && 'release' || 'build --snapshot' }} ${{ github.event_name == 'pull_request' && '--single-target' || '' }} --clean + args: build --snapshot --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GOPATH: ${{ needs.checks.outputs.go_path }} @@ -242,10 +265,6 @@ jobs: AWS_NAP_WAF_PUB_KEY: ${{ secrets.AWS_NAP_WAF_PUB_KEY }} AWS_NAP_WAF_DOS_PRODUCT_CODE: ${{ secrets.AWS_NAP_WAF_DOS_PRODUCT_CODE }} AWS_NAP_WAF_DOS_PUB_KEY: ${{ secrets.AWS_NAP_WAF_DOS_PUB_KEY }} - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_COMMUNITY }} - AZURE_STORAGE_ACCOUNT: ${{ secrets.AZURE_STORAGE_ACCOUNT }} - AZURE_STORAGE_KEY: ${{ secrets.AZURE_STORAGE_KEY }} - AZURE_BUCKET_NAME: ${{ secrets.AZURE_BUCKET_NAME }} GORELEASER_CURRENT_TAG: "v${{ needs.checks.outputs.ic_version }}" if: ${{ needs.checks.outputs.binary_cache_hit != 'true' }} @@ -256,24 +275,156 @@ jobs: key: nginx-ingress-${{ needs.checks.outputs.go_code_md5 }} if: ${{ needs.checks.outputs.binary_cache_hit != 'true' }} + build-docker: + name: Build Docker OSS + needs: [binaries, checks] + strategy: + fail-fast: false + matrix: + image: [debian, alpine] + platforms: + ["linux/arm, linux/arm64, linux/amd64, linux/ppc64le, linux/s390x"] + include: + - image: ubi + platforms: "linux/arm64, linux/amd64, linux/ppc64le, linux/s390x" + uses: ./.github/workflows/build-oss.yml + with: + platforms: ${{ matrix.platforms }} + image: ${{ matrix.image }} + go-md5: ${{ needs.checks.outputs.go_code_md5 }} + base-image-md5: ${{ needs.checks.outputs.docker_md5 }} + authenticated: ${{ needs.checks.outputs.forked_workflow != 'true' }} + full-build: ${{ inputs.force && inputs.force || false }} + tag: ${{ needs.checks.outputs.build_tag }} + branch: ${{ github.head_ref && github.head_ref || github.ref }} + permissions: + contents: read + actions: read + security-events: write + id-token: write + packages: write + secrets: inherit + + build-docker-plus: + name: Build Docker Plus + needs: [binaries, checks] + strategy: + fail-fast: false + matrix: + image: [debian-plus, alpine-plus, alpine-plus-fips] + platforms: ["linux/arm64, linux/amd64"] + target: [goreleaser, aws] + include: + - image: ubi-plus + platforms: "linux/arm64, linux/amd64, linux/s390x" + target: goreleaser + uses: ./.github/workflows/build-plus.yml + with: + platforms: ${{ matrix.platforms }} + image: ${{ matrix.image }} + target: ${{ matrix.target }} + go-md5: ${{ needs.checks.outputs.go_code_md5 }} + base-image-md5: ${{ needs.checks.outputs.docker_md5 }} + branch: ${{ github.head_ref && github.head_ref || github.ref }} + tag: ${{ needs.checks.outputs.build_tag }} + authenticated: ${{ needs.checks.outputs.forked_workflow != 'true' }} + full-build: ${{ inputs.force && inputs.force || false }} + permissions: + contents: read + security-events: write + id-token: write + secrets: inherit + + build-docker-nap: + name: Build Docker NAP + needs: [binaries, checks] + strategy: + fail-fast: false + matrix: + image: [debian-plus-nap] + platforms: ["linux/amd64"] + target: [goreleaser, aws] + nap_modules: [dos, waf, "waf,dos"] + include: + - image: ubi-9-plus-nap + target: goreleaser + platforms: "linux/amd64" + nap_modules: waf + - image: ubi-8-plus-nap + target: goreleaser + platforms: "linux/amd64" + nap_modules: dos + - image: ubi-8-plus-nap + target: goreleaser + platforms: "linux/amd64" + nap_modules: "waf,dos" + - image: ubi-9-plus-nap + target: aws + platforms: "linux/amd64" + nap_modules: waf + - image: ubi-8-plus-nap + target: aws + platforms: "linux/amd64" + nap_modules: dos + - image: ubi-8-plus-nap + target: aws + platforms: "linux/amd64" + nap_modules: "waf,dos" + - image: alpine-plus-nap-fips + target: goreleaser + platforms: "linux/amd64" + nap_modules: waf + - image: alpine-plus-nap-v5-fips + target: goreleaser + platforms: "linux/amd64" + nap_modules: waf + - image: debian-plus-nap-v5 + target: goreleaser + platforms: "linux/amd64" + nap_modules: waf + - image: ubi-9-plus-nap-v5 + target: goreleaser + platforms: "linux/amd64" + nap_modules: waf + - image: ubi-8-plus-nap-v5 + target: goreleaser + platforms: "linux/amd64" + nap_modules: waf + uses: ./.github/workflows/build-plus.yml + with: + platforms: ${{ matrix.platforms }} + image: ${{ matrix.image }} + target: ${{ matrix.target }} + go-md5: ${{ needs.checks.outputs.go_code_md5 }} + base-image-md5: ${{ needs.checks.outputs.docker_md5 }} + branch: ${{ github.head_ref && github.head_ref || github.ref }} + tag: ${{ needs.checks.outputs.build_tag }} + nap-modules: ${{ matrix.nap_modules }} + authenticated: ${{ needs.checks.outputs.forked_workflow != 'true' }} + full-build: ${{ inputs.force && inputs.force || false }} + permissions: + contents: read + security-events: write + id-token: write + secrets: inherit + helm-tests: if: ${{ needs.checks.outputs.docs_only != 'true' }} name: Helm Tests ${{ matrix.base-os }} runs-on: ubuntu-22.04 - needs: [checks, binaries] + needs: [checks, binaries, build-docker, build-docker-plus] strategy: + fail-fast: false matrix: include: - base-os: debian image: gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/nginx-ic/nginx-ingress tag: ${{ needs.checks.outputs.build_tag }} type: oss - platforms: "linux/arm, linux/arm64, linux/amd64, linux/ppc64le, linux/s390x" - base-os: debian-plus image: gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/nginx-ic/nginx-plus-ingress tag: ${{ needs.checks.outputs.build_tag }} type: plus - platforms: "linux/arm64, linux/amd64" permissions: contents: read id-token: write @@ -281,21 +432,6 @@ jobs: - name: Checkout Repository uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - - name: Fetch Cached Artifacts - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 - with: - path: ${{ github.workspace }}/dist - key: nginx-ingress-${{ needs.checks.outputs.go_code_md5 }} - - - name: Docker Buildx - uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 - - - name: Setup QEMU - uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 - with: - platforms: ${{ matrix.platforms }} - if: ${{ needs.checks.outputs.forked_workflow == 'false' }} - - name: Authenticate to Google Cloud id: auth uses: google-github-actions/auth@71fee32a0bb7e97b4d33d548e7d957010649d8fa # v2.1.3 @@ -303,7 +439,7 @@ jobs: token_format: access_token workload_identity_provider: ${{ secrets.GCR_WORKLOAD_IDENTITY }} service_account: ${{ secrets.GCR_SERVICE_ACCOUNT }} - if: ${{ needs.checks.outputs.forked_workflow == 'false' }} + if: ${{ needs.checks.outputs.forked_workflow == 'false' || needs.checks.outputs.docs_only == 'false' }} - name: Login to GCR uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 @@ -311,61 +447,56 @@ jobs: registry: gcr.io username: oauth2accesstoken password: ${{ steps.auth.outputs.access_token }} - if: ${{ needs.checks.outputs.forked_workflow == 'false' }} + if: ${{ needs.checks.outputs.forked_workflow == 'false' || needs.checks.outputs.docs_only == 'false' }} - - name: Check if base images exist - id: base_exists + - name: Check if stable image exists + id: stable_exists run: | - if docker manifest inspect gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/nginx-ic-base/${{ matrix.type }}:${{ needs.checks.outputs.docker_md5 }}-${{ matrix.base-os }}; then - echo "exists=0" >> $GITHUB_OUTPUT - else - echo "exists=1" >> $GITHUB_OUTPUT + if docker pull ${{ matrix.image }}:${{ needs.checks.outputs.stable_tag }}; then + echo "exists=true" >> $GITHUB_OUTPUT fi - if: ${{ needs.checks.outputs.forked_workflow == 'false' }} + if: ${{ needs.checks.outputs.forked_workflow == 'false' || needs.checks.outputs.docs_only == 'false' }} + + - name: Pull build image + run: | + docker pull ${{ matrix.image }}:${{ needs.checks.outputs.build_tag }} + if: ${{ ( needs.checks.outputs.forked_workflow == 'false' || needs.checks.outputs.docs_only == 'false' ) && steps.stable_exists.outputs.exists != 'true' }} - - name: Build Base Container - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 + - name: Fetch Cached Artifacts + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: - file: build/Dockerfile - context: "." - cache-from: type=gha,scope=${{ matrix.base-os }} - cache-to: type=gha,scope=${{ matrix.base-os }},mode=max - target: common - tags: gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/nginx-ic-base/${{ matrix.type }}:${{ needs.checks.outputs.docker_md5 }}-${{ matrix.base-os }} - platforms: ${{ matrix.platforms }} - pull: true - push: true - build-args: | - BUILD_OS=${{ matrix.base-os }} - IC_VERSION=${{ needs.checks.outputs.ic_version }} - secrets: | - ${{ matrix.type == 'plus' && format('"nginx-repo.crt={0}"', secrets.NGINX_CRT) || '' }} - ${{ matrix.type == 'plus' && format('"nginx-repo.key={0}"', secrets.NGINX_KEY) || '' }} - if: ${{ needs.checks.outputs.forked_workflow == 'false' && steps.base_exists.outputs.exists != 0 }} + path: ${{ github.workspace }}/dist + key: nginx-ingress-${{ needs.checks.outputs.go_code_md5 }} + if: ${{ needs.checks.outputs.forked_workflow == 'true' && needs.checks.outputs.docs_only == 'false' }} + + - name: Docker Buildx + uses: docker/setup-buildx-action@2b51285047da1547ffb1b2203d8be4c0af6b1f20 # v3.2.0 + if: ${{ needs.checks.outputs.forked_workflow == 'true' && needs.checks.outputs.docs_only == 'false' }} - name: Build Docker Image ${{ matrix.base-os }} - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 + uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0 with: file: build/Dockerfile context: "." cache-from: type=gha,scope=${{ matrix.base-os }} - target: goreleaser${{ needs.checks.outputs.forked_workflow == 'false' && '-prebuilt' || '' }} + target: goreleaser tags: "${{ matrix.image }}:${{ matrix.tag }}" pull: true load: true build-args: | BUILD_OS=${{ matrix.base-os }} - PREBUILT_BASE_IMG=gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/nginx-ic-base/${{ matrix.type }}:${{ needs.checks.outputs.docker_md5 }}-${{ matrix.base-os }} IC_VERSION=CI secrets: | ${{ matrix.type == 'plus' && format('"nginx-repo.crt={0}"', secrets.NGINX_CRT) || '' }} ${{ matrix.type == 'plus' && format('"nginx-repo.key={0}"', secrets.NGINX_KEY) || '' }} + if: ${{ needs.checks.outputs.forked_workflow == 'true' && needs.checks.outputs.docs_only == 'false' }} - name: Deploy Kubernetes id: k8s run: | kind create cluster --name ${{ github.run_id }} --image=kindest/node:v${{ needs.checks.outputs.k8s_latest }} --wait 75s - kind load docker-image ${{ matrix.image }}:${{ matrix.tag }} --name ${{ github.run_id }} + kind load docker-image "${{ matrix.image }}:${{ matrix.tag }}" --name ${{ github.run_id }} + if: ${{ steps.stable_exists.outputs.exists != 'true' && needs.checks.outputs.docs_only == 'false' }} - name: Install Chart run: > @@ -379,10 +510,12 @@ jobs: --set controller.telemetryReporting.enable=false --wait working-directory: ${{ github.workspace }}/charts/nginx-ingress + if: ${{ steps.stable_exists.outputs.exists != 'true' && needs.checks.outputs.docs_only == 'false' }} - name: Expose Test Ingresses run: | kubectl port-forward service/${{ matrix.type }}-nginx-ingress-controller 8080:80 8443:443 & + if: ${{ steps.stable_exists.outputs.exists != 'true' && needs.checks.outputs.docs_only == 'false' }} - name: Test HTTP run: | @@ -394,6 +527,7 @@ jobs: fi printf '.'; counter=$(($counter+1)); sleep 5; done + if: ${{ steps.stable_exists.outputs.exists != 'true' && needs.checks.outputs.docs_only == 'false' }} - name: Test HTTPS run: | @@ -405,9 +539,10 @@ jobs: fi printf '.'; counter=$(($counter+1)); sleep 5; done + if: ${{ steps.stable_exists.outputs.exists != 'true' && needs.checks.outputs.docs_only == 'false' }} setup-matrix: - if: ${{ needs.checks.outputs.docs_only != 'true' }} + if: ${{ inputs.force || needs.checks.outputs.docs_only != 'true' }} name: Setup Matrix for Smoke Tests runs-on: ubuntu-22.04 needs: [binaries, checks] @@ -422,11 +557,7 @@ jobs: - id: set-matrix run: | - if [ "${{ github.event_name }}" == "schedule" ]; then - echo "matrix=$(cat .github/data/matrix-regression.json | jq -c --argjson latest '${{ needs.checks.outputs.latest_kindest_node_versions }}' '.k8s += $latest')" >> $GITHUB_OUTPUT - else - echo "matrix=$(cat .github/data/matrix-smoke.json | jq -c --arg latest "${{ needs.checks.outputs.k8s_latest }}" '.k8s += [$latest]')" >> $GITHUB_OUTPUT - fi + echo "matrix=$(cat .github/data/matrix-smoke.json | jq -c --arg latest "${{ needs.checks.outputs.k8s_latest }}" '.k8s += [$latest]')" >> $GITHUB_OUTPUT - name: Docker Buildx uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 @@ -438,7 +569,7 @@ jobs: token_format: access_token workload_identity_provider: ${{ secrets.GCR_WORKLOAD_IDENTITY }} service_account: ${{ secrets.GCR_SERVICE_ACCOUNT }} - if: ${{ needs.checks.outputs.forked_workflow == 'false' }} + if: ${{ needs.checks.outputs.forked_workflow == 'false' && needs.checks.outputs.docs_only == 'false' }} - name: Login to GCR uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 @@ -446,18 +577,18 @@ jobs: registry: gcr.io username: oauth2accesstoken password: ${{ steps.auth.outputs.access_token }} - if: ${{ needs.checks.outputs.forked_workflow == 'false' }} + if: ${{ needs.checks.outputs.forked_workflow == 'false' && needs.checks.outputs.docs_only == 'false' }} - name: Check if test image exists id: check-image run: | - docker manifest inspect "gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/test-runner:${{ hashFiles('./tests/requirements.txt') || 'latest' }}" + docker pull gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/test-runner:${{ hashFiles('./tests/requirements.txt') || 'latest' }} shell: bash continue-on-error: true - if: ${{ needs.checks.outputs.forked_workflow == 'false' }} + if: ${{ needs.checks.outputs.forked_workflow == 'false' && needs.checks.outputs.docs_only == 'false' }} - name: Build Test-Runner Container - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 + uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0 with: file: tests/Dockerfile context: "." @@ -466,13 +597,18 @@ jobs: pull: true push: ${{ needs.checks.outputs.forked_workflow == 'false' }} load: false - if: ${{ steps.check-image.outcome == 'failure' }} + if: ${{ steps.check-image.outcome == 'failure' && needs.checks.outputs.docs_only == 'false' }} smoke-tests: - if: ${{ needs.checks.outputs.docs_only != 'true' }} + if: ${{ inputs.force || needs.checks.outputs.docs_only != 'true' }} name: ${{ matrix.images.label }} ${{ matrix.images.image }} ${{ matrix.k8s }} smoke tests runs-on: ubuntu-22.04 - needs: [checks, setup-matrix] + needs: + - checks + - setup-matrix + - build-docker + - build-docker-plus + - build-docker-nap strategy: fail-fast: false matrix: ${{ fromJSON(needs.setup-matrix.outputs.matrix) }} @@ -490,15 +626,6 @@ jobs: echo "build_tag=${{ needs.checks.outputs.build_tag }}${{ contains(matrix.images.image, 'ubi') && '-ubi' || '' }}${{ contains(matrix.images.image, 'alpine') && '-alpine' || '' }}${{ contains(matrix.images.target, 'aws') && '-mktpl' || '' }}${{ contains(matrix.images.image, 'fips') && '-fips' || ''}}" >> $GITHUB_OUTPUT echo "stable_tag=${{ needs.checks.outputs.stable_tag }}${{ contains(matrix.images.image, 'ubi') && '-ubi' || '' }}${{ contains(matrix.images.image, 'alpine') && '-alpine' || '' }}${{ contains(matrix.images.target, 'aws') && '-mktpl' || '' }}${{ contains(matrix.images.image, 'fips') && '-fips' || ''}}" >> $GITHUB_OUTPUT - - name: NAP modules - id: nap_modules - run: | - [[ "${{ matrix.images.nap_modules }}" == "waf,dos" ]] && modules="waf-dos" || name="${{ matrix.images.nap_modules }}" - [[ "${{ matrix.images.nap_modules }}" == "waf,dos" ]] && modules="both" || modules="${{ matrix.images.nap_modules }}" - echo "modules=${modules}" >> $GITHUB_OUTPUT - echo "name=${name}" >> $GITHUB_OUTPUT - if: ${{ matrix.images.nap_modules }} - - name: Authenticate to Google Cloud id: auth uses: google-github-actions/auth@71fee32a0bb7e97b4d33d548e7d957010649d8fa # v2.1.3 @@ -506,7 +633,7 @@ jobs: token_format: access_token workload_identity_provider: ${{ secrets.GCR_WORKLOAD_IDENTITY }} service_account: ${{ secrets.GCR_SERVICE_ACCOUNT }} - if: ${{ needs.checks.outputs.forked_workflow == 'false' }} + if: ${{ needs.checks.outputs.forked_workflow == 'false' && needs.checks.outputs.docs_only == 'false' }} - name: Login to GCR uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 @@ -514,47 +641,27 @@ jobs: registry: gcr.io username: oauth2accesstoken password: ${{ steps.auth.outputs.access_token }} - if: ${{ needs.checks.outputs.forked_workflow == 'false' }} + if: ${{ needs.checks.outputs.forked_workflow == 'false' && needs.checks.outputs.docs_only == 'false' }} - - name: Check if base images exist - id: base_exists + - name: Check if stable image exists + id: stable_exists run: | - if docker manifest inspect gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/nginx-ic-base/${{ matrix.images.type }}:${{ needs.checks.outputs.docker_md5 }}-${{ matrix.images.image }}${{ matrix.images.nap_modules && format('-{0}', steps.nap_modules.outputs.name) || '' }}; then - echo "exists=0" >> $GITHUB_OUTPUT - else - echo "exists=1" >> $GITHUB_OUTPUT + if docker pull ${{ steps.image_details.outputs.name }}:${{ steps.image_details.outputs.stable_tag }}; then + echo "exists=true" >> $GITHUB_OUTPUT fi - if: ${{ needs.checks.outputs.forked_workflow == 'false' }} + if: ${{ needs.checks.outputs.forked_workflow == 'false' && needs.checks.outputs.docs_only == 'false' }} - - name: Docker Buildx - uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 - - - name: Setup QEMU - uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 - with: - platforms: ${{ matrix.platforms }} + - name: NAP modules + id: nap_modules + run: | + [[ "${{ matrix.images.nap_modules }}" == "waf,dos" ]] && modules="waf-dos" || modules="${{ matrix.images.nap_modules }}" + echo "modules=${modules}" >> $GITHUB_OUTPUT + if: ${{ matrix.images.nap_modules }} - - name: Build Base Container - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 - with: - file: build/Dockerfile - context: "." - cache-from: type=gha,scope=${{ matrix.images.image }}${{ matrix.images.nap_modules && format('-{0}', steps.nap_modules.outputs.name) || '' }} - cache-to: type=gha,scope=${{ matrix.images.image }}${{ matrix.images.nap_modules && format('-{0}', steps.nap_modules.outputs.name) || '' }},mode=max - target: common - tags: gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/nginx-ic-base/${{ matrix.images.type }}:${{ needs.checks.outputs.docker_md5 }}-${{ matrix.images.image }}${{ matrix.images.nap_modules && format('-{0}', steps.nap_modules.outputs.name) || '' }} - platforms: ${{ matrix.images.platforms }} - pull: true - push: true - build-args: | - BUILD_OS=${{ matrix.images.image }} - IC_VERSION=${{ needs.checks.outputs.ic_version }} - ${{ steps.nap_modules.outputs.modules != '' && format('NAP_MODULES={0}', steps.nap_modules.outputs.modules) || '' }} - secrets: | - ${{ contains(matrix.images.image, 'nap') && format('"nginx-repo.crt={0}"', secrets.NGINX_AP_CRT) || format('"nginx-repo.crt={0}"', secrets.NGINX_CRT) }} - ${{ contains(matrix.images.image, 'nap') && format('"nginx-repo.key={0}"', secrets.NGINX_AP_KEY) || format('"nginx-repo.key={0}"', secrets.NGINX_KEY) }} - ${{ contains(matrix.images.image, 'ubi') && format('"rhel_license={0}"', secrets.RHEL_LICENSE) || '' }} - if: ${{ needs.checks.outputs.forked_workflow == 'false' && steps.base_exists.outputs.exists != 0 }} + - name: Pull build image + run: | + docker pull ${{ steps.image_details.outputs.name }}:${{ steps.image_details.outputs.build_tag }} + if: ${{ needs.checks.outputs.forked_workflow == 'false' && steps.stable_exists.outputs.exists != 'true' && needs.checks.outputs.docs_only == 'false' }} - name: Fetch Cached Artifacts uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -562,6 +669,7 @@ jobs: path: ${{ github.workspace }}/dist key: nginx-ingress-${{ needs.checks.outputs.go_code_md5 }} fail-on-cache-miss: true + if: ${{ needs.checks.outputs.forked_workflow == 'true' && needs.checks.outputs.docs_only == 'false' }} - name: Check if test image exists id: check-image @@ -569,10 +677,10 @@ jobs: docker manifest inspect "gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/test-runner:${{ hashFiles('./tests/requirements.txt') || 'latest' }}" shell: bash continue-on-error: true - if: ${{ needs.checks.outputs.forked_workflow == 'false' }} + if: ${{ needs.checks.outputs.forked_workflow == 'false' && needs.checks.outputs.docs_only == 'false' }} - name: Build Test-Runner Container - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 + uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0 with: file: tests/Dockerfile context: "." @@ -581,29 +689,29 @@ jobs: pull: true push: ${{ needs.checks.outputs.forked_workflow == 'false' }} load: ${{ needs.checks.outputs.forked_workflow == 'true' }} - if: ${{ needs.checks.outputs.forked_workflow == 'true' || steps.check-image.outcome == 'failure' }} + if: ${{ ( needs.checks.outputs.forked_workflow == 'true' || steps.check-image.outcome == 'failure' ) && needs.checks.outputs.docs_only == 'false' }} - name: Build ${{ matrix.images.image }} Container - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 + uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0 with: file: build/Dockerfile context: "." cache-from: type=gha,scope=${{ matrix.images.image }}${{ steps.nap_modules.outputs.name != '' && format('-{0}', steps.nap_modules.outputs.name) || '' }} cache-to: type=gha,scope=${{ matrix.images.image }}${{ steps.nap_modules.outputs.name != '' && format('-{0}', steps.nap_modules.outputs.name) || '' }},mode=max - target: goreleaser${{ needs.checks.outputs.forked_workflow == 'false' && '-prebuilt' || '' }} + target: goreleaser tags: "${{ steps.image_details.outputs.name }}:${{ steps.image_details.outputs.build_tag }}" load: true pull: true build-args: | BUILD_OS=${{ matrix.images.image }} IC_VERSION=CI - PREBUILT_BASE_IMG=gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/nginx-ic-base/${{ matrix.images.type }}:${{ needs.checks.outputs.docker_md5 }}-${{ matrix.images.image }}${{ steps.nap_modules.outputs.name != '' && format('-{0}', steps.nap_modules.outputs.name) || '' }} ${{ contains(matrix.images.image, 'nap') && format('NAP_MODULES={0}', steps.nap_modules.outputs.modules) || '' }} ${{ contains(matrix.images.marker, 'appprotect') && 'DEBIAN_VERSION=buster-slim' || '' }} secrets: | ${{ contains(matrix.images.image, 'nap') && format('"nginx-repo.crt={0}"', secrets.NGINX_AP_CRT) || format('"nginx-repo.crt={0}"', secrets.NGINX_CRT) }} ${{ contains(matrix.images.image, 'nap') && format('"nginx-repo.key={0}"', secrets.NGINX_AP_KEY) || format('"nginx-repo.key={0}"', secrets.NGINX_KEY) }} ${{ contains(matrix.images.image, 'ubi') && format('"rhel_license={0}"', secrets.RHEL_LICENSE) || '' }} + if: ${{ needs.checks.outputs.forked_workflow == 'true' && needs.checks.outputs.docs_only == 'false' }} - name: Run Smoke Tests id: smoke-tests @@ -616,6 +724,7 @@ jobs: k8s-version: ${{ matrix.k8s }} azure-ad-secret: ${{ secrets.AZURE_AD_AUTOMATION }} test-image: "gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/test-runner:${{ hashFiles('./tests/requirements.txt') || 'latest' }}" + if: ${{ steps.stable_exists.outputs.exists != 'true' && needs.checks.outputs.docs_only == 'false' }} - name: Upload Test Results uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 @@ -638,195 +747,32 @@ jobs: exit 1 fi - build-docker: - if: ${{ needs.checks.outputs.docs_only != 'true' }} - name: Build Docker OSS + tag-stable: + name: Tag tested image as stable needs: [checks, smoke-results] - strategy: - fail-fast: false - matrix: - image: [debian, alpine] - platforms: - ["linux/arm, linux/arm64, linux/amd64, linux/ppc64le, linux/s390x"] - include: - - image: ubi - platforms: "linux/arm64, linux/amd64, linux/ppc64le, linux/s390x" - uses: ./.github/workflows/build-oss.yml - with: - platforms: ${{ matrix.platforms }} - image: ${{ matrix.image }} - go-md5: ${{ needs.checks.outputs.go_code_md5 }} - publish-image: ${{ needs.checks.outputs.publish_images == 'true' }} - base-image-md5: ${{ needs.checks.outputs.docker_md5 }} - forked-workflow: ${{ needs.checks.outputs.forked_workflow == 'true' }} - permissions: - contents: read - actions: read - security-events: write - id-token: write - packages: write - secrets: inherit - - build-docker-plus: - if: ${{ needs.checks.outputs.docs_only != 'true' }} - name: Build Docker Plus - needs: [checks, smoke-results, release-notes] - strategy: - fail-fast: false - matrix: - image: [debian-plus, alpine-plus, alpine-plus-fips] - platforms: ["linux/arm64, linux/amd64"] - target: [goreleaser, aws] - include: - - image: ubi-plus - platforms: "linux/arm64, linux/amd64, linux/s390x" - target: goreleaser - uses: ./.github/workflows/build-plus.yml - with: - platforms: ${{ matrix.platforms }} - image: ${{ matrix.image }} - target: ${{ matrix.target }} - go-md5: ${{ needs.checks.outputs.go_code_md5 }} - base-image-md5: ${{ needs.checks.outputs.docker_md5 }} - release-url: ${{ needs.release-notes.outputs.release-url }} - publish-image: ${{ needs.checks.outputs.publish_images == 'true' }} - publish-aws-market-place: ${{ (github.ref_type == 'tag' && vars.OLD_RELEASE_FLOW == 'true') && startsWith(github.ref, 'refs/heads/release-') && contains(matrix.target, 'aws') }} - publish-nginx-reqistry: ${{ needs.checks.outputs.publish_images == 'true' && ! contains(matrix.target, 'aws') }} - forked-workflow: ${{ needs.checks.outputs.forked_workflow == 'true' }} permissions: - contents: read - security-events: write - id-token: write - secrets: inherit - - build-docker-nap: - if: ${{ needs.checks.outputs.docs_only != 'true' }} - name: Build Docker NAP - needs: [checks, smoke-results, release-notes] - strategy: - fail-fast: false - matrix: - image: [debian-plus-nap] - platforms: ["linux/amd64"] - target: [goreleaser, aws] - nap_modules: [dos, waf, "waf,dos"] - include: - - image: ubi-9-plus-nap - target: goreleaser - platforms: "linux/amd64" - nap_modules: waf - - image: ubi-8-plus-nap - target: goreleaser - platforms: "linux/amd64" - nap_modules: dos - - image: ubi-8-plus-nap - target: goreleaser - platforms: "linux/amd64" - nap_modules: "waf,dos" - - image: ubi-9-plus-nap - target: aws - platforms: "linux/amd64" - nap_modules: waf - - image: ubi-8-plus-nap - target: aws - platforms: "linux/amd64" - nap_modules: dos - - image: ubi-8-plus-nap - target: aws - platforms: "linux/amd64" - nap_modules: "waf,dos" - - image: alpine-plus-nap-fips - target: goreleaser - platforms: "linux/amd64" - nap_modules: waf - - image: alpine-plus-nap-v5-fips - target: goreleaser - platforms: "linux/amd64" - nap_modules: waf - - image: debian-plus-nap-v5 - target: goreleaser - platforms: "linux/amd64" - nap_modules: waf - - image: ubi-9-plus-nap-v5 - target: goreleaser - platforms: "linux/amd64" - nap_modules: waf - - image: ubi-8-plus-nap-v5 - target: goreleaser - platforms: "linux/amd64" - nap_modules: waf - uses: ./.github/workflows/build-plus.yml + contents: read # To checkout repository + id-token: write # To sign into Google Container Registry + uses: ./.github/workflows/retag-images.yml with: - platforms: ${{ matrix.platforms }} - image: ${{ matrix.image }} - target: ${{ matrix.target }} - go-md5: ${{ needs.checks.outputs.go_code_md5 }} - base-image-md5: ${{ needs.checks.outputs.docker_md5 }} - nap_modules: ${{ matrix.nap_modules }} - release-url: ${{ needs.release-notes.outputs.release-url }} - publish-image: ${{ needs.checks.outputs.publish_images == 'true' }} - publish-aws-market-place: ${{ (github.ref_type == 'tag' && vars.OLD_RELEASE_FLOW == 'true') && startsWith(github.ref, 'refs/heads/release-') && contains(matrix.target, 'aws') }} - publish-nginx-reqistry: ${{ needs.checks.outputs.publish_images == 'true' && ! contains(matrix.target, 'aws') }} - forked-workflow: ${{ needs.checks.outputs.forked_workflow == 'true' }} - permissions: - contents: read - security-events: write - id-token: write + source_tag: ${{ needs.checks.outputs.build_tag }} + target_tag: ${{ needs.checks.outputs.stable_tag }} + dry_run: false secrets: inherit - - publish-helm: - name: Package and Publish Helm Chart - needs: [checks, helm-tests] - if: ${{ github.event_name == 'push' && ! startsWith(github.ref, 'refs/heads/release-') }} + if: ${{ inputs.force || (needs.checks.outputs.forked_workflow == 'false' && needs.smoke-results.result == 'success' && needs.checks.outputs.stable_image_exists != 'true' && needs.checks.outputs.docs_only == 'false') }} + + trigger-image-promotion: + name: Promote images on Force Run + needs: + - build-docker + - build-docker-plus + - build-docker-nap + - smoke-results permissions: contents: write # for pushing to Helm Charts repository + id-token: write # To sign into Google Container Registry + actions: read packages: write # for helm to push to GHCR - uses: ./.github/workflows/publish-helm.yml - with: - branch: ${{ github.ref_name }} - ic_version: ${{ (github.ref_type == 'tag' && vars.OLD_RELEASE_FLOW == 'true') && needs.checks.outputs.ic_version || 'edge' }} - chart_version: ${{ (github.ref_type == 'tag' && vars.OLD_RELEASE_FLOW == 'true') && needs.checks.outputs.chart_version || '0.0.0-edge' }} - nginx_helm_repo: ${{ github.ref_type == 'tag' && vars.OLD_RELEASE_FLOW == 'true' }} - secrets: inherit - - operator: - name: Trigger PR for Operator - runs-on: ubuntu-22.04 - needs: [checks, publish-helm] - steps: - - name: - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - with: - github-token: ${{ secrets.NGINX_PAT }} - script: | - await github.rest.actions.createWorkflowDispatch({ - owner: context.repo.owner, - repo: 'nginx-ingress-helm-operator', - workflow_id: 'sync-chart.yml', - ref: 'main', - inputs: { - chart_version: '${{ needs.checks.outputs.chart_version }}' - }, - }) - if: ${{ github.ref_type == 'tag' && vars.OLD_RELEASE_FLOW == 'true' }} - - gcp-marketplace: - name: Trigger PR for GCP Marketplace - runs-on: ubuntu-22.04 - needs: [checks, build-docker-plus, build-docker-nap] - steps: - - name: - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - with: - github-token: ${{ secrets.NGINX_PAT }} - script: | - await github.rest.actions.createWorkflowDispatch({ - owner: context.repo.owner, - repo: 'kubernetes-ingress-gcp', - workflow_id: 'sync-chart.yml', - ref: 'main', - inputs: { - chart_version: '${{ needs.checks.outputs.chart_version }}' - }, - }) - if: ${{ github.ref_type == 'tag' && vars.OLD_RELEASE_FLOW == 'true' }} + security-events: write + uses: ./.github/workflows/image-promotion.yml + if: ${{ inputs.force && inputs.force || false }} diff --git a/.github/workflows/create-release-tag.yml b/.github/workflows/create-release-tag.yml deleted file mode 100644 index 9b1ce39b07..0000000000 --- a/.github/workflows/create-release-tag.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: "Create Tag on release branch" - -on: - workflow_dispatch: - inputs: - release_branch: - required: true - type: string - default: 'release-0.0' - tag: - required: false - type: string - default: 'vx.x.x' - dry_run: - type: boolean - default: false - - -defaults: - run: - shell: bash - -permissions: - contents: read - -jobs: - create: - name: Create Tag on release branch in NIC repo - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Checkout NIC repo - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - with: - ref: ${{ inputs.release_branch }} - - - name: Create new release Tag - run: | - branch="${{ inputs.release_branch }}" - tag="${{ inputs.tag }}" - if git rev-parse --verify refs/tags/${tag}; then - echo "Adding tag ${tag}." - git tag -a ${tag} -m "Version ${tag#v*}" - echo "Pushing to tag ${tag} to branch ${branch}" - if ! ${{ inputs.dry_run }}; then - git push origin "${tag}" - else - echo "DRY RUN not making any changes" - git push --dry-run origin "${tag}" - fi - else - echo "Warning: Tag ${tag} already exists. Not making any changes" - fi - env: - GITHUB_TOKEN: ${{ secrets.NGINX_PAT }} diff --git a/.github/workflows/image-promotion.yml b/.github/workflows/image-promotion.yml new file mode 100644 index 0000000000..e3983ee0e7 --- /dev/null +++ b/.github/workflows/image-promotion.yml @@ -0,0 +1,328 @@ +# This workflow will: +# - build images for forked workflows +# - tag stable for forked workflows +# - tag edge for main workflows +# - tag release branch name for release branch workflows +# - release edge images & helm charts for edge + +on: + push: + branches: + - main + - release-* + workflow_call: + +defaults: + run: + shell: bash + +concurrency: + group: ${{ github.ref_name }}-image-promotion + cancel-in-progress: true + +permissions: + contents: read + +jobs: + checks: + name: Checks and variables + runs-on: ubuntu-22.04 + permissions: + contents: read + id-token: write + outputs: + go_path: ${{ steps.vars.outputs.go_path }} + go_code_md5: ${{ steps.vars.outputs.go_code_md5 }} + binary_cache_hit: ${{ steps.binary-cache.outputs.cache-hit }} + chart_version: ${{ steps.vars.outputs.chart_version }} + ic_version: ${{ steps.vars.outputs.ic_version }} + docker_md5: ${{ steps.vars.outputs.docker_md5 }} + build_tag: ${{ steps.vars.outputs.build_tag }} + stable_tag: ${{ steps.vars.outputs.stable_tag }} + stable_image_exists: ${{ steps.stable_exists.outputs.exists }} + steps: + - name: Checkout Repository + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + + - name: Setup Golang Environment + uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + with: + go-version-file: go.mod + + - name: Output Variables + id: vars + run: | + echo "go_path=$(go env GOPATH)" >> $GITHUB_OUTPUT + source .github/data/version.txt + echo "ic_version=${IC_VERSION}" >> $GITHUB_OUTPUT + echo "chart_version=${HELM_CHART_VERSION}" >> $GITHUB_OUTPUT + ./.github/scripts/variables.sh go_code_md5 >> $GITHUB_OUTPUT + ./.github/scripts/variables.sh docker_md5 >> $GITHUB_OUTPUT + ./.github/scripts/variables.sh build_tag >> $GITHUB_OUTPUT + ./.github/scripts/variables.sh stable_tag >> $GITHUB_OUTPUT + cat $GITHUB_OUTPUT + + - name: Fetch Cached Binary Artifacts + id: binary-cache + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + with: + path: ${{ github.workspace }}/dist + key: nginx-ingress-${{ steps.vars.outputs.go_code_md5 }} + lookup-only: true + + - name: Authenticate to Google Cloud + id: auth + uses: google-github-actions/auth@55bd3a7c6e2ae7cf1877fd1ccb9d54c0503c457c # v2.1.2 + with: + token_format: access_token + workload_identity_provider: ${{ secrets.GCR_WORKLOAD_IDENTITY }} + service_account: ${{ secrets.GCR_SERVICE_ACCOUNT }} + + - name: Login to GCR + uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0 + with: + registry: gcr.io + username: oauth2accesstoken + password: ${{ steps.auth.outputs.access_token }} + + - name: Check if stable image exists + id: stable_exists + run: | + if docker pull gcr.io/f5-gcs-7899-ptg-ingrss-ctlr/dev/nginx-ic/nginx-ingress:${{ steps.vars.outputs.stable_tag }}; then + echo "exists=true" >> $GITHUB_OUTPUT + fi + + build-docker: + if: ${{ needs.checks.outputs.stable_image_exists != 'true' }} + name: Build Docker OSS + needs: [checks] + strategy: + fail-fast: false + matrix: + image: [debian, alpine] + platforms: + ["linux/arm, linux/arm64, linux/amd64, linux/ppc64le, linux/s390x"] + include: + - image: ubi + platforms: "linux/arm64, linux/amd64, linux/ppc64le, linux/s390x" + uses: ./.github/workflows/build-oss.yml + with: + platforms: ${{ matrix.platforms }} + image: ${{ matrix.image }} + go-md5: ${{ needs.checks.outputs.go_code_md5 }} + base-image-md5: ${{ needs.checks.outputs.docker_md5 }} + authenticated: true + tag: ${{ needs.checks.outputs.build_tag }} + branch: ${{ github.ref }} + permissions: + contents: read + actions: read + security-events: write + id-token: write + packages: write + secrets: inherit + + build-docker-plus: + if: ${{ needs.checks.outputs.stable_image_exists != 'true' }} + name: Build Docker Plus + needs: [checks] + strategy: + fail-fast: false + matrix: + image: [debian-plus, alpine-plus, alpine-plus-fips] + platforms: ["linux/arm64, linux/amd64"] + target: [goreleaser, aws] + include: + - image: ubi-plus + platforms: "linux/arm64, linux/amd64, linux/s390x" + target: goreleaser + uses: ./.github/workflows/build-plus.yml + with: + platforms: ${{ matrix.platforms }} + image: ${{ matrix.image }} + target: ${{ matrix.target }} + go-md5: ${{ needs.checks.outputs.go_code_md5 }} + base-image-md5: ${{ needs.checks.outputs.docker_md5 }} + authenticated: true + tag: ${{ needs.checks.outputs.build_tag }} + branch: ${{ github.ref }} + permissions: + contents: read + actions: read + security-events: write + id-token: write + packages: write + secrets: inherit + + build-docker-nap: + if: ${{ needs.checks.outputs.stable_image_exists != 'true' }} + name: Build Docker NAP + needs: [checks] + strategy: + fail-fast: false + matrix: + image: [debian-plus-nap] + platforms: ["linux/amd64"] + target: [goreleaser, aws] + nap_modules: [dos, waf, "waf,dos"] + include: + - image: ubi-9-plus-nap + target: goreleaser + platforms: "linux/amd64" + nap_modules: waf + - image: ubi-8-plus-nap + target: goreleaser + platforms: "linux/amd64" + nap_modules: dos + - image: ubi-8-plus-nap + target: goreleaser + platforms: "linux/amd64" + nap_modules: "waf,dos" + - image: ubi-9-plus-nap + target: aws + platforms: "linux/amd64" + nap_modules: waf + - image: ubi-8-plus-nap + target: aws + platforms: "linux/amd64" + nap_modules: dos + - image: ubi-8-plus-nap + target: aws + platforms: "linux/amd64" + nap_modules: "waf,dos" + - image: alpine-plus-nap-fips + target: goreleaser + platforms: "linux/amd64" + nap_modules: waf + - image: alpine-plus-nap-v5-fips + target: goreleaser + platforms: "linux/amd64" + nap_modules: waf + - image: debian-plus-nap-v5 + target: goreleaser + platforms: "linux/amd64" + nap_modules: waf + - image: ubi-9-plus-nap-v5 + target: goreleaser + platforms: "linux/amd64" + nap_modules: waf + - image: ubi-8-plus-nap-v5 + target: goreleaser + platforms: "linux/amd64" + nap_modules: waf + uses: ./.github/workflows/build-plus.yml + with: + platforms: ${{ matrix.platforms }} + image: ${{ matrix.image }} + target: ${{ matrix.target }} + go-md5: ${{ needs.checks.outputs.go_code_md5 }} + base-image-md5: ${{ needs.checks.outputs.docker_md5 }} + nap-modules: ${{ matrix.nap_modules }} + authenticated: true + tag: ${{ needs.checks.outputs.build_tag }} + branch: ${{ github.ref }} + permissions: + contents: read + actions: read + security-events: write + id-token: write + packages: write + secrets: inherit + + tag-stable: + if: ${{ needs.checks.outputs.stable_image_exists != 'true' }} + name: Tag build image as stable + needs: [checks, build-docker, build-docker-plus, build-docker-nap] + permissions: + contents: read # To checkout repository + id-token: write # To sign into Google Container Registry + uses: ./.github/workflows/retag-images.yml + with: + source_tag: ${{ needs.checks.outputs.build_tag }} + target_tag: ${{ needs.checks.outputs.stable_tag }} + dry_run: false + secrets: inherit + + tag-edge: + name: Tag tested image as stable + needs: [checks, build-docker, build-docker-plus, build-docker-nap] + permissions: + contents: read # To checkout repository + id-token: write # To sign into Google Container Registry + uses: ./.github/workflows/retag-images.yml + with: + source_tag: ${{ needs.checks.outputs.stable_tag }} + target_tag: ${{ github.ref_name == github.event.repository.default_branch && 'edge' || github.ref_name }} + dry_run: false + secrets: inherit + + release-oss: + if: ${{ github.ref_name == github.event.repository.default_branch }} + name: Release Docker OSS + needs: [checks, build-docker] + uses: ./.github/workflows/oss-release.yml + with: + gcr_release_registry: false + ecr_public_registry: true + dockerhub_public_registry: true + quay_public_registry: true + github_public_registry: true + source_tag: ${{ needs.checks.outputs.stable_tag }} + target_tag: "edge" + dry_run: false + permissions: + contents: read + id-token: write + packages: write + secrets: inherit + + release-plus: + if: ${{ github.ref_name == github.event.repository.default_branch }} + name: Release Docker Plus + needs: [checks, build-docker-plus, build-docker-nap] + uses: ./.github/workflows/plus-release.yml + with: + nginx_registry: true + gcr_release_registry: false + gcr_mktpl_registry: false + ecr_mktpl_registry: false + az_mktpl_registry: false + source_tag: ${{ needs.checks.outputs.stable_tag }} + target_tag: "edge" + dry_run: false + permissions: + contents: read + id-token: write + secrets: inherit + + publish-helm-chart: + if: ${{ github.ref_name == github.event.repository.default_branch }} + name: Publish Helm Chart + needs: [checks] + uses: ./.github/workflows/publish-helm.yml + with: + branch: ${{ github.ref_name }} + ic_version: 0.0.0-edge + chart_version: edge + nginx_helm_repo: false + permissions: + contents: write # for pushing to Helm Charts repository + packages: write # for helm to push to GHCR + secrets: inherit + + certify-openshift-images: + if: ${{ github.ref_name == github.event.repository.default_branch }} + name: Certify OpenShift UBI images + runs-on: ubuntu-22.04 + needs: [release-oss] + steps: + - name: Checkout Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Certify UBI OSS images in quay + uses: ./.github/actions/certify-openshift-image + continue-on-error: true + with: + image: quay.io/nginx/nginx-ingress:edge-ubi + project_id: ${{ secrets.CERTIFICATION_PROJECT_ID }} + pyxis_token: ${{ secrets.PYXIS_API_TOKEN }} diff --git a/.github/workflows/notifications.yml b/.github/workflows/notifications.yml index 16631422f4..ab7304833f 100644 --- a/.github/workflows/notifications.yml +++ b/.github/workflows/notifications.yml @@ -12,6 +12,7 @@ on: - "OpenSSF Scorecards" - "Build OSS" - "Build Plus" + - "Release NIC" types: - completed diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b69e6b78cb..e3aa48d963 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,5 @@ name: Release NIC +run-name: ${{ inputs.dry_run && '[DRY RUN] ' || '' }}Release NIC ${{ inputs.nic_version }} from ${{ inputs.release_branch }} by @${{ github.actor }} on: workflow_dispatch: diff --git a/.github/workflows/update-docker-sha.yml b/.github/workflows/update-docker-sha.yml index 7d08b9a74f..603c2afd15 100644 --- a/.github/workflows/update-docker-sha.yml +++ b/.github/workflows/update-docker-sha.yml @@ -37,7 +37,7 @@ jobs: id: vars run: | source_branch=main - if ${{ inputs.source_branch }}; then + if [ -n "${{ inputs.source_branch }}" ]; then source_branch=${{ inputs.source_branch }} fi echo "source_branch=${source_branch}" >> $GITHUB_OUTPUT diff --git a/.github/workflows/updates-notification.yml b/.github/workflows/updates-notification.yml index 6168dbffef..102045956f 100644 --- a/.github/workflows/updates-notification.yml +++ b/.github/workflows/updates-notification.yml @@ -12,6 +12,9 @@ on: image_digest: required: true type: string + slack_webhook_url: + required: true + type: string defaults: run: @@ -79,4 +82,4 @@ jobs: }] } env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} + SLACK_WEBHOOK_URL: ${{ inputs.slack_webhook_url }} diff --git a/build/Dockerfile b/build/Dockerfile index 223dac5ccb..ea07f3b626 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -116,6 +116,8 @@ USER 101 FROM alpine:3.19@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b as alpine-plus ARG NGINX_PLUS_VERSION +ENV NGINX_VERSION=${NGINX_PLUS_VERSION} + RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/apk/cert.pem,mode=0644 \ --mount=type=secret,id=nginx-repo.key,dst=/etc/apk/cert.key,mode=0644 \ --mount=type=bind,from=alpine-opentracing-lib,target=/tmp/ot/ \ @@ -131,6 +133,9 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/apk/cert.pem,mode=0644 \ ############################################# Base image for Alpine with NGINX Plus and FIPS ############################################# FROM alpine-plus as alpine-plus-fips +ARG NGINX_PLUS_VERSION + +ENV NGINX_VERSION=${NGINX_PLUS_VERSION} RUN --mount=type=bind,from=alpine-fips-3.19,target=/tmp/fips/ \ mkdir -p /usr/ssl \ @@ -143,6 +148,9 @@ RUN --mount=type=bind,from=alpine-fips-3.19,target=/tmp/fips/ \ FROM alpine:3.17@sha256:53cf9478b76f4c8fae126acbdfb79bed6e69e628faff572ebe4a029d3d247d98 as alpine-plus-nap-fips ARG NGINX_PLUS_VERSION ARG NGINX_AGENT +ARG NGINX_PLUS_VERSION + +ENV NGINX_VERSION=${NGINX_PLUS_VERSION} RUN --mount=type=bind,from=alpine-fips-3.17,target=/tmp/fips/ \ --mount=type=secret,id=nginx-repo.crt,dst=/etc/apk/cert.pem,mode=0644 \ @@ -176,6 +184,9 @@ RUN --mount=type=bind,from=alpine-fips-3.17,target=/tmp/fips/ \ FROM alpine:3.17@sha256:53cf9478b76f4c8fae126acbdfb79bed6e69e628faff572ebe4a029d3d247d98 as alpine-plus-nap-v5-fips ARG NGINX_PLUS_VERSION ARG NGINX_AGENT +ARG NGINX_PLUS_VERSION + +ENV NGINX_VERSION=${NGINX_PLUS_VERSION} RUN --mount=type=bind,from=alpine-fips-3.17,target=/tmp/fips/ \ --mount=type=secret,id=nginx-repo.crt,dst=/etc/apk/cert.pem,mode=0644 \ @@ -205,6 +216,9 @@ RUN --mount=type=bind,from=alpine-fips-3.17,target=/tmp/fips/ \ ############################################# Base image for Debian with NGINX Plus ############################################# FROM debian:12-slim@sha256:67f3931ad8cb1967beec602d8c0506af1e37e8d73c2a0b38b181ec5d8560d395 AS debian-plus +ARG NGINX_PLUS_VERSION + +ENV NGINX_VERSION=${NGINX_PLUS_VERSION} SHELL ["/bin/bash", "-o", "pipefail", "-c"] RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \ @@ -230,6 +244,9 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode FROM debian:11-slim@sha256:acc5810124f0929ab44fc7913c0ad936b074cbd3eadf094ac120190862ba36c4 as debian-plus-nap ARG NAP_MODULES ARG NGINX_AGENT +ARG NGINX_PLUS_VERSION + +ENV NGINX_VERSION=${NGINX_PLUS_VERSION} RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \ --mount=type=secret,id=nginx-repo.key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \ @@ -293,6 +310,9 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode FROM debian-plus as debian-plus-nap-v5 ARG NAP_MODULES ARG NGINX_AGENT +ARG NGINX_PLUS_VERSION + +ENV NGINX_VERSION=${NGINX_PLUS_VERSION} RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \ --mount=type=secret,id=nginx-repo.key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \ @@ -319,6 +339,9 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode ############################################# Base image for UBI with NGINX Plus ############################################# FROM ubi-minimal AS ubi-plus +ARG NGINX_PLUS_VERSION + +ENV NGINX_VERSION=${NGINX_PLUS_VERSION} SHELL ["/bin/bash", "-o", "pipefail", "-c"] RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \ @@ -339,6 +362,9 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode FROM ubi-minimal as ubi-9-plus-nap ARG NAP_MODULES ARG NGINX_AGENT +ARG NGINX_PLUS_VERSION + +ENV NGINX_VERSION=${NGINX_PLUS_VERSION} RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \ --mount=type=secret,id=nginx-repo.key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \ @@ -380,6 +406,9 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode FROM ubi-minimal as ubi-9-plus-nap-v5 ARG NAP_MODULES ARG NGINX_AGENT +ARG NGINX_PLUS_VERSION + +ENV NGINX_VERSION=${NGINX_PLUS_VERSION} RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \ --mount=type=secret,id=nginx-repo.key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \ @@ -422,6 +451,9 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode FROM redhat/ubi8@sha256:143123d85045df426c5bbafc6863659880ebe276eb02c77ee868b88d08dbd05d as ubi-8-plus-nap ARG NAP_MODULES ARG NGINX_AGENT +ARG NGINX_PLUS_VERSION + +ENV NGINX_VERSION=${NGINX_PLUS_VERSION} RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \ --mount=type=secret,id=nginx-repo.key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \ @@ -483,6 +515,9 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode FROM redhat/ubi8@sha256:143123d85045df426c5bbafc6863659880ebe276eb02c77ee868b88d08dbd05d as ubi-8-plus-nap-v5 ARG NAP_MODULES ARG NGINX_AGENT +ARG NGINX_PLUS_VERSION + +ENV NGINX_VERSION=${NGINX_PLUS_VERSION} RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \ --mount=type=secret,id=nginx-repo.key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \ @@ -531,7 +566,6 @@ ARG BUILD_OS ARG IC_VERSION ARG TARGETPLATFORM ARG NAP_MODULES=none -ARG NGINX_PLUS_VERSION RUN --mount=type=bind,target=/tmp \ --mount=type=bind,from=nginx-files,src=common.sh,target=/usr/local/bin/common.sh \ @@ -551,7 +585,7 @@ LABEL org.opencontainers.image.version="${IC_VERSION}" \ org.opencontainers.image.vendor="NGINX Inc " \ org.nginx.kic.image.build.target="${TARGETPLATFORM}" \ org.nginx.kic.image.build.os="${BUILD_OS}" \ - org.nginx.kic.image.build.nginx.version="${NGINX_PLUS_VERSION}${NGINX_VERSION}" + org.nginx.kic.image.build.nginx.version="${NGINX_VERSION}" ############################################# Build nginx-ingress in golang container ############################################# @@ -667,6 +701,7 @@ USER 101 ############################################# Builder style stage to avoid duplicate layers for ingress and ingress with setcap ############################################# +# Builder image for goreleaser FROM common AS goreleaser-setcap ARG TARGETARCH @@ -684,13 +719,23 @@ LABEL org.nginx.kic.image.build.version="goreleaser" COPY --link --chown=101:0 --from=goreleaser-setcap /nginx-ingress / +############################################# Builder style stage to avoid duplicate layers for ingress and ingress with setcap ############################################# +# Builder image for goreleaser-prebuilt +FROM ${PREBUILT_BASE_IMG} AS goreleaser-setcap-prebuilt +ARG TARGETARCH + +COPY --link --chown=101:0 dist/kubernetes-ingress_linux_${TARGETARCH}*/nginx-ingress / +USER 0 +RUN setcap 'cap_net_bind_service=+ep' /nginx-ingress && setcap -v 'cap_net_bind_service=+ep' /nginx-ingress + + ############################################# Create image with nginx-ingress built by GoReleaser & using prebuilt base image ############################################# FROM ${PREBUILT_BASE_IMG} AS goreleaser-prebuilt ARG TARGETARCH LABEL org.nginx.kic.image.build.version="goreleaser" -COPY --link --chown=101:0 --from=goreleaser-setcap /nginx-ingress / +COPY --link --chown=101:0 --from=goreleaser-setcap-prebuilt /nginx-ingress / # root is required for `setcap` invocation USER 0 RUN --mount=type=bind,target=/tmp [ -z "${BUILD_OS##*plus*}" ] && PLUS=-plus; cp -a /tmp/internal/configs/version1/nginx$PLUS.ingress.tmpl /tmp/internal/configs/version1/nginx$PLUS.tmpl \ @@ -701,6 +746,7 @@ USER 101 ############################################# Builder style stage to avoid duplicate layers for ingress and ingress with setcap ############################################# +# Builder image for aws FROM common AS aws-setcap ARG TARGETARCH ARG NAP_MODULES_AWS @@ -720,6 +766,17 @@ LABEL org.nginx.kic.image.build.version="aws" COPY --link --chown=101:0 --from=aws-setcap /nginx-ingress / +############################################# Builder style stage to avoid duplicate layers for ingress and ingress with setcap ############################################# +# Builder image for aws-prebuilt +FROM ${PREBUILT_BASE_IMG} AS aws-setcap-prebuilt +ARG TARGETARCH +ARG NAP_MODULES_AWS + +COPY --link --chown=101:0 dist/aws*${NAP_MODULES_AWS}_linux_${TARGETARCH}*/nginx-ingress / +USER 0 +RUN setcap 'cap_net_bind_service=+ep' /nginx-ingress && setcap -v 'cap_net_bind_service=+ep' /nginx-ingress + + ############################################# Create image with nginx-ingress built by GoReleaser for AWS Marketplace ############################################# FROM ${PREBUILT_BASE_IMG} AS aws-prebuilt ARG TARGETARCH @@ -727,7 +784,7 @@ ARG NAP_MODULES_AWS LABEL org.nginx.kic.image.build.version="aws" -COPY --link --chown=101:0 --from=aws-setcap /nginx-ingress / +COPY --link --chown=101:0 --from=aws-setcap-prebuilt /nginx-ingress / USER 0 RUN --mount=type=bind,target=/tmp [ -z "${BUILD_OS##*plus*}" ] && PLUS=-plus; cp -a /tmp/internal/configs/version1/nginx$PLUS.ingress.tmpl /tmp/internal/configs/version1/nginx$PLUS.tmpl \ /tmp/internal/configs/version2/nginx$PLUS.virtualserver.tmpl /tmp/internal/configs/version2/nginx$PLUS.transportserver.tmpl / \ diff --git a/tests/Dockerfile b/tests/Dockerfile index c8cf24f6ec..6e721e87e8 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -3,7 +3,7 @@ FROM kindest/node:v1.30.0@sha256:047357ac0cfea04663786a612ba1eaba9702bef25227a794b52890dd8bcd692e # this is here so we can grab the latest version of skopeo and have dependabot keep it up to date -FROM quay.io/skopeo/stable:v1.15.1@sha256:981737142fd7ac5315561fc1e39d42203b6efa97ddd2870bde0204b791574efa +FROM quay.io/skopeo/stable:v1.15.1 FROM python:3.12@sha256:00389e020fe42a6c74a8f091ce9b28324422d084efdff26eabe93bc4ae9a110b