From 0657f6aa5a41744f7e02e58853d826370835060b Mon Sep 17 00:00:00 2001 From: Shubhadapaithankar Date: Thu, 19 Sep 2024 09:41:44 -0700 Subject: [PATCH] Shubhadapaithankar/aro-9323-cirp-mvp (#3799) - Converted containerized CI process to use docker for ease of use in ADO - Added stage to authenticate and push CI images to ACR - Added support for extracting test results and coverage files from containerized build --- .pipelines/ci.yml | 310 ++++++++++----------- .pipelines/templates/template-acr-push.yml | 68 +++++ Dockerfile.ci-rp | 12 +- Makefile | 38 ++- 4 files changed, 238 insertions(+), 190 deletions(-) create mode 100644 .pipelines/templates/template-acr-push.yml diff --git a/.pipelines/ci.yml b/.pipelines/ci.yml index 25f117d4c1c..2acee837b27 100644 --- a/.pipelines/ci.yml +++ b/.pipelines/ci.yml @@ -27,167 +27,149 @@ variables: - template: vars.yml stages: -- stage: NotContainerized - dependsOn: Containerized - jobs: - - job: Python_Unit_Tests - pool: - name: 1es-aro-ci-pool - variables: - HOME: $(Agent.BuildDirectory) - - steps: - - template: ./templates/template-checkout.yml - - script: | - set -xe - make test-python - [[ -z "$(git status -s)" ]] - displayName: ๐ŸงชRun Python Unit Tests - target: python - - - job: Golang_Unit_Tests - pool: - name: 1es-aro-ci-pool - variables: - GOCACHE: /tmp/gocache - steps: - - template: ./templates/template-checkout.yml - - - script: | - set -xe - go version - go env - displayName: Print Go version & env - target: golang - - - script: | - echo "##vso[task.prependpath]$(go env GOPATH)/bin" - displayName: Add GOBIN to path - target: golang - - - script: | - set -xe - # Required for podman 5 - sudo tdnf install -y gpgme-devel lvm2-devel btrfs-progs-devel golang-1.21.11-1.cm2 - make generate - [[ -z "$(git status -s)" ]] - displayName: โš™๏ธ Run Golang code generate - target: golang - - - script: | - set -xe - make build-all - [[ -z "$(git status -s)" ]] - displayName: ๐Ÿ•ต๏ธ Build Golang code - target: golang - - - script: | - set -xe - make unit-test-go - displayName: ๐Ÿงช Run Golang unit tests - target: golang - - - task: PublishTestResults@2 - displayName: ๐Ÿ“Š Publish tests results - inputs: - testResultsFiles: $(System.DefaultWorkingDirectory)/**/report.xml - condition: succeededOrFailed() - - - script: | - make xmlcov - displayName: โš™๏ธ Process Reports - condition: succeededOrFailed() - target: golang - - - task: PublishCodeCoverageResults@1 - displayName: ๐Ÿ“ˆ Publish code coverage - inputs: - codeCoverageTool: Cobertura - summaryFileLocation: $(System.DefaultWorkingDirectory)/**/coverage.xml - failIfCoverageEmpty: false - condition: succeededOrFailed() - target: golang - - - job: Lint_Admin_Portal - pool: - name: 1es-aro-ci-pool - steps: - - template: ./templates/template-checkout.yml - - script: | - set -xe - make lint-admin-portal - displayName: ๐Ÿงน Lint Admin Portal -- stage: Containerized - dependsOn: [] - jobs: - - job: Build_Lint_Test_RP_And_Portal - pool: - name: 1es-aro-ci-pool - steps: - - template: ./templates/template-checkout.yml - - script: | - set -xe - export NO_CACHE=true - - # Install podman - sudo rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm - sudo dnf install -y podman make - which podman - podman --version - whoami - - # Start podman service - . ./hack/e2e/utils.sh - run_podman - validate_podman_running - - # run build - export PODMAN_REMOTE_ARGS="-r --url=tcp://localhost:8888" - make ci-rp - - # Stop podman - kill_podman - target: ubi8 - - - job: Build_and_Test_Az_ARO_Extension - pool: - name: 1es-aro-ci-pool - steps: - - template: ./templates/template-checkout.yml - - script: | - set -xe - export NO_CACHE=true - - # Install podman - sudo rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm - sudo dnf install -y podman make - which podman - podman --version - whoami - - # Start podman service - . ./hack/e2e/utils.sh - run_podman - validate_podman_running - - # run build - export PODMAN_REMOTE_ARGS="-r --url=tcp://localhost:8888" - make ci-azext-aro - - # Stop podman - kill_podman - target: ubi8 - - - job: Lint_Az_ARO_Extension - pool: - name: 1es-aro-ci-pool - variables: - HOME: $(Agent.BuildDirectory) - steps: - - template: ./templates/template-checkout.yml - - script: | - set -xe - export AZDEV_CONFIG_DIR=$(Agent.BuildDirectory)/azdev-config - make test-python - [[ -z "$(git status -s)" ]] - target: python + - stage: NotContainerized + dependsOn: Containerized + jobs: + - job: Python_Unit_Tests + pool: + name: 1es-aro-ci-pool + variables: + HOME: $(Agent.BuildDirectory) + + steps: + - template: ./templates/template-checkout.yml + - script: | + set -xe + make test-python + [[ -z "$(git status -s)" ]] + displayName: ๐Ÿงช Run Python Unit Tests + target: python + + - job: Golang_Unit_Tests + pool: + name: 1es-aro-ci-pool + variables: + GOCACHE: /tmp/gocache + steps: + - template: ./templates/template-checkout.yml + + - script: | + set -xe + go version + go env + displayName: Print Go version & env + target: golang + + - script: | + echo "##vso[task.prependpath]$(go env GOPATH)/bin" + displayName: Add GOBIN to path + target: golang + + - script: | + set -xe + # Required for podman 5 + sudo tdnf install -y gpgme-devel lvm2-devel btrfs-progs-devel golang-1.21.11-1.cm2 + make generate + [[ -z "$(git status -s)" ]] + displayName: โš™๏ธ Run Golang code generate + target: golang + + - script: | + set -xe + make build-all + [[ -z "$(git status -s)" ]] + displayName: ๐Ÿ•ต๏ธ Build Golang code + target: golang + + - script: | + set -xe + make unit-test-go + displayName: ๐Ÿงช Run Golang unit tests + target: golang + + - script: | + make xmlcov + displayName: โš™๏ธ Process Reports + condition: succeededOrFailed() + target: golang + + - job: Lint_Admin_Portal + pool: + name: 1es-aro-ci-pool + steps: + - template: ./templates/template-checkout.yml + - script: | + set -xe + make lint-admin-portal + displayName: ๐Ÿงน Lint Admin Portal + + - stage: Containerized + dependsOn: [] + jobs: + - job: Build_Test_And_Push_Az_ARO_Extension + pool: + name: 1es-aro-ci-pool + steps: + - template: ./templates/template-checkout.yml + + # Build and test the Az ARO Extension + - script: | + set -xe + DOCKER_BUILD_CI_ARGS="--load" make ci-azext-aro VERSION=$(Build.BuildId) + displayName: ๐Ÿ›  Build & Test Az ARO Extension + + # Push the image to ACR + - template: ./templates/template-acr-push.yml + parameters: + acrFQDN: 'arosvcdev.azurecr.io' + repository: 'azext-aro' + pushLatest: true + + - job: Build_Lint_Test_RP_And_Portal + pool: + name: 1es-aro-ci-pool + steps: + - template: ./templates/template-checkout.yml + + # Build and test RP and Portal + - script: | + set -xe + DOCKER_BUILD_CI_ARGS="--load" make ci-rp VERSION=$(Build.BuildId) + displayName: ๐Ÿ›  Build & Test RP and Portal + + # Publish test results + - task: PublishTestResults@2 + displayName: ๐Ÿ“Š Publish tests results + inputs: + testResultsFiles: $(System.DefaultWorkingDirectory)/report.xml + condition: succeededOrFailed() + + # Publish code coverage results + - task: PublishCodeCoverageResults@2 + displayName: ๐Ÿ“ˆ Publish code coverage + inputs: + codeCoverageTool: Cobertura + summaryFileLocation: $(System.DefaultWorkingDirectory)/coverage.xml + failIfCoverageEmpty: false + condition: succeededOrFailed() + + # Push the image to ACR + - template: ./templates/template-acr-push.yml + parameters: + acrFQDN: 'arosvcdev.azurecr.io' + repository: 'aro' + pushLatest: true + + - job: Lint_Az_ARO_Extension + pool: + name: 1es-aro-ci-pool + variables: + HOME: $(Agent.BuildDirectory) + steps: + - template: ./templates/template-checkout.yml + - script: | + set -xe + export AZDEV_CONFIG_DIR=$(Agent.BuildDirectory)/azdev-config + make test-python + [[ -z "$(git status -s)" ]] + target: python diff --git a/.pipelines/templates/template-acr-push.yml b/.pipelines/templates/template-acr-push.yml new file mode 100644 index 00000000000..aadf07132a2 --- /dev/null +++ b/.pipelines/templates/template-acr-push.yml @@ -0,0 +1,68 @@ +# This template is used to authenticate to Azure and push Docker images across Azure tenants. +# We cannot use the simpler Docker@2 push command because MSI does not support cross-tenant authentication. + +parameters: + - name: acrFQDN + type: string + - name: repository # This is both the ACR and local repository name + type: string # The local and ACR image repository name + - name: pushLatest + type: boolean + default: false + +steps: + # Authenticate to ACR and push the image + - task: AzureCLI@2 + displayName: 'Authenticate to Azure and Push Docker Image' + inputs: + azureSubscription: 'ado-pipeline-dev-image-push' # Service connection name + scriptType: bash + scriptLocation: 'inlineScript' + inlineScript: | + set -xe + echo "Authenticating to Azure and ACR..." + ACR_FQDN="${{ parameters.acrFQDN }}" + REGISTRY_NAME=$(echo $ACR_FQDN | cut -d'.' -f1) + + # Login to ACR + az acr login --name $REGISTRY_NAME + + # List Docker images to verify the built image exists + echo "Listing Docker images..." + docker images + + # Ensure the image is available locally before tagging the build ID + IMAGE_NAME="${{ parameters.repository }}:$(Build.BuildId)" + if [[ "$(docker images -q $IMAGE_NAME 2> /dev/null)" == "" ]]; then + echo "Error: Image $IMAGE_NAME not found. Exiting." + exit 1 + fi + + # Ensure the image is available locally before tagging 'latest' + IMAGE_LATEST="${{ parameters.repository }}:latest" + if [[ "$(docker images -q $IMAGE_LATEST 2> /dev/null)" == "" ]]; then + echo "Warning: Image $IMAGE_LATEST not found. Skipping latest tag." + SKIP_LATEST=true + else + SKIP_LATEST=false + fi + + # Tag the image with the ACR repository for the build ID + echo "Tagging image with build ID..." + docker tag $IMAGE_NAME ${ACR_FQDN}/${{ parameters.repository }}:$(Build.BuildId) + + # If the latest image exists, tag it as well + if [ "$SKIP_LATEST" == "false" ]; then + echo "Tagging image with 'latest'..." + docker tag $IMAGE_LATEST ${ACR_FQDN}/${{ parameters.repository }}:latest + fi + + # Push the Docker image to ACR with build ID + echo "Pushing image with build ID to ACR..." + docker push ${ACR_FQDN}/${{ parameters.repository }}:$(Build.BuildId) + + # Optionally push the image as 'latest' + if [ "${{ parameters.pushLatest }}" == "true" ] && [ "$SKIP_LATEST" == "false" ]; then + echo "Pushing 'latest' tag to ACR..." + docker push ${ACR_FQDN}/${{ parameters.repository }}:latest + fi diff --git a/Dockerfile.ci-rp b/Dockerfile.ci-rp index 50ac917ed8d..2ea0dd7b299 100644 --- a/Dockerfile.ci-rp +++ b/Dockerfile.ci-rp @@ -11,13 +11,14 @@ USER root # Copying package files and installing dependencies COPY portal/v2/package*.json ./ -RUN npm ci -RUN npm audit --audit-level high --omit=dev # Run audit without dev dependencies + +# Combine npm install and audit in one step to reduce layers and clean cache +RUN npm ci \ + && npm audit --audit-level high --omit=dev # Copying the rest of the source and build COPY --chown=root:root portal/v2/ ./ RUN npm run lint && npm run build -LABEL stage="portal-build-cache-layer" ############################################################################### # Stage 2: Compile the Golang RP code @@ -54,14 +55,15 @@ COPY --from=portal-build /build/pkg/portal/assets/v2/build /app/pkg/portal/asset # Build RP and E2E test suite bins RUN go build -ldflags "-X github.com/Azure/ARO-RP/pkg/util/version.GitCommit=${ARO_VERSION}" ./cmd/aro + RUN go test ./test/e2e/... -tags e2e,codec.safe -c -ldflags "-X github.com/Azure/ARO-RP/pkg/util/version.GitCommit=${ARO_VERSION}" -o e2e.test # Additional tests -RUN gotestsum --format pkgname --junitfile report.xml -- -coverprofile=cover.out ./... +RUN gotestsum --format pkgname --junitfile report.xml -- -coverprofile=cover.out ./... \ + && gocov convert cover.out | gocov-xml > coverage.xml # Validate FIPS RUN hack/fips/validate-fips.sh ./aro -LABEL stage="rp-build-cache-layer" ############################################################################### # Stage 3: final is our slim image with minimal layers and tools diff --git a/Makefile b/Makefile index 3264fa75e52..e19e9475259 100644 --- a/Makefile +++ b/Makefile @@ -370,6 +370,7 @@ NO_CACHE ?= true # that service as a URL (see .pipelines/ci.yml). This should be invoked on all # use of `podman` in the Makefile. PODMAN_REMOTE_ARGS ?= +DOCKER_BUILD_CI_ARGS ?= # Image names that will be found in the local podman image registry after build # (tags are always VERSION). @@ -384,8 +385,7 @@ LOCAL_TUNNEL_IMAGE ?= aro-tunnel ############################################################################### .PHONY: ci-azext-aro ci-azext-aro: - podman $(PODMAN_REMOTE_ARGS) \ - build . \ + docker build . $(DOCKER_BUILD_CI_ARGS) \ -f Dockerfile.ci-azext-aro \ --platform=linux/amd64 \ --no-cache=$(NO_CACHE) \ @@ -399,32 +399,28 @@ ci-clean: .PHONY: ci-rp ci-rp: fix-macos-vendor - podman $(PODMAN_REMOTE_ARGS) \ - build . \ + docker build . $(DOCKER_BUILD_CI_ARGS) \ -f Dockerfile.ci-rp \ --ulimit=nofile=4096:4096 \ --build-arg REGISTRY=$(REGISTRY) \ --build-arg ARO_VERSION=$(VERSION) \ --no-cache=$(NO_CACHE) \ + --target=builder \ + -t $(LOCAL_ARO_RP_BUILD_IMAGE):$(VERSION) + + docker build . $(DOCKER_BUILD_CI_ARGS) \ + -f Dockerfile.ci-rp \ + --ulimit=nofile=4096:4096 \ + --build-arg REGISTRY=$(REGISTRY) \ + --build-arg ARO_VERSION=$(VERSION) \ -t $(LOCAL_ARO_RP_IMAGE):$(VERSION) - # Tag the portal build image if it exists - @PORTAL_IMAGE_ID=$(shell podman $(PODMAN_REMOTE_ARGS) image ls --filter label=stage=portal-build-cache-layer --noheading --format "{{.Id}}" | tail -n 1); \ - if [ -n "$$PORTAL_IMAGE_ID" ]; then \ - echo "Tagging Portal Image $$PORTAL_IMAGE_ID as $(LOCAL_ARO_PORTAL_BUILD_IMAGE):$(VERSION)"; \ - podman $(PODMAN_REMOTE_ARGS) tag $$PORTAL_IMAGE_ID $(LOCAL_ARO_PORTAL_BUILD_IMAGE):$(VERSION); \ - else \ - echo "No Portal Image found with label stage=portal-build-cache-layer"; \ - fi - - # Tag the RP build image if it exists - @RP_IMAGE_ID=$(shell podman $(PODMAN_REMOTE_ARGS) image ls --filter label=stage=rp-build-cache-layer --noheading --format "{{.Id}}" | tail -n 1); \ - if [ -n "$$RP_IMAGE_ID" ]; then \ - echo "Tagging RP Image $$RP_IMAGE_ID as $(LOCAL_ARO_RP_BUILD_IMAGE):$(VERSION)"; \ - podman $(PODMAN_REMOTE_ARGS) tag $$RP_IMAGE_ID $(LOCAL_ARO_RP_BUILD_IMAGE):$(VERSION); \ - else \ - echo "No RP Image found with label stage=rp-build-cache-layer"; \ - fi + # Extract test coverage files from build to local filesystem + docker create --name extract_cover_out $(LOCAL_ARO_RP_BUILD_IMAGE):$(VERSION); \ + docker cp extract_cover_out:/app/report.xml ./report.xml; \ + docker cp extract_cover_out:/app/coverage.xml ./coverage.xml; \ + docker rm extract_cover_out; + .PHONY: ci-tunnel ci-tunnel: fix-macos-vendor