diff --git a/.devops/build.yml b/.devops/build.yml index f6577b2..c7c8c9e 100644 --- a/.devops/build.yml +++ b/.devops/build.yml @@ -13,6 +13,7 @@ trigger: exclude: - .devops - .github/workflows + pr: none parameters: @@ -20,20 +21,25 @@ parameters: type: string - name: feed type: string - - name: package + - name: az_package type: string + displayName: "Oras package name" + - name: dryrun + displayName: "Execute a dry run?" + type: boolean + default: false - name: jobs type: object default: azurelinux_8: - new_LTS_image: false + hasExistingImages: true distro: azurelinux version: 8 package: temurin-8 image: "image-repository" tag: "3.0" distroless_8: - new_LTS_image: false + hasExistingImages: true distro: distroless version: 8 package: temurin-8 @@ -42,21 +48,21 @@ parameters: base_image: "image-repository" base_tag: "3.0" ubuntu_11: - new_LTS_image: false + hasExistingImages: true distro: ubuntu version: 11 package: msopenjdk-11 image: "image-repository" tag: "image-tag" azurelinux_11: - new_LTS_image: false + hasExistingImages: true distro: azurelinux version: 11 package: msopenjdk-11 image: "image-repository" tag: "3.0" distroless_11: - new_LTS_image: false + hasExistingImages: true distro: distroless version: 11 package: msopenjdk-11 @@ -65,21 +71,21 @@ parameters: base_image: "image-repository" base_tag: "3.0" ubuntu_17: - new_LTS_image: false + hasExistingImages: true distro: ubuntu version: 17 package: msopenjdk-17 image: "image-repository" tag: "image-tag" azurelinux_17: - new_LTS_image: false + hasExistingImages: true distro: azurelinux version: 17 package: msopenjdk-17 image: "image-repository" tag: "3.0" distroless_17: - new_LTS_image: false + hasExistingImages: true distro: distroless version: 17 package: msopenjdk-17 @@ -88,21 +94,21 @@ parameters: base_image: "image-repository" base_tag: "3.0" ubuntu_21: - new_LTS_image: false + hasExistingImages: true distro: ubuntu version: 21 package: msopenjdk-21 image: "image-repository" tag: "image-tag" azurelinux_21: - new_LTS_image: false + hasExistingImages: true distro: azurelinux version: 21 package: msopenjdk-21 image: "image-repository" tag: "3.0" distroless_21: - new_LTS_image: false + hasExistingImages: true distro: distroless version: 21 package: msopenjdk-21 @@ -127,70 +133,70 @@ extends: os: windows stages: - stage: build_internal - displayName: "Build Internal" + displayName: Build Internal + variables: + ACR_NAME: msopenjdk jobs: - job: build_internal - displayName: "build internal" pool: name: JEG-azurelinux-x64-release os: linux strategy: matrix: ${{ parameters.jobs }} steps: - - task: AzureCLI@2 - displayName: "Download ORAS" - condition: ne( variables['new_LTS_image'], true) - inputs: - azureSubscription: "JEG-Infrastructure" - scriptType: "bash" - scriptLocation: "scriptPath" - scriptPath: $(Build.SourcesDirectory)/scripts/install-oras.sh - env: - AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) - ORAS_VERSION: 1.1.0 - ORGANIZATION: ${{ parameters.organization }} - FEED: ${{ parameters.feed }} - NAME: ${{ parameters.package }} + - template: /.devops/templates/prepare-annotation.yml@self + parameters: + registry: $(INTERNAL_ACR_REGISTRY) + tag: $(version)-$(distro) - bash: | - REGISTRIES=msopenjdk.azurecr.io/internal/private/openjdk/jdk:$(version)-$(distro) + REGISTRIES=$(INTERNAL_ACR_REGISTRY):$(version)-$(distro) if [[ "$(distro)" == "azurelinux" ]]; then - REGISTRIES+=";msopenjdk.azurecr.io/internal/private/openjdk/jdk:$(version)-mariner" + REGISTRIES+=";$(INTERNAL_ACR_REGISTRY):$(version)-mariner" fi echo "##vso[task.setvariable variable=REGISTRIES]$REGISTRIES" displayName: Set REGISTRIES variable - - task: AzureCLI@2 - displayName: Annotate previous image - condition: ne( variables['new_LTS_image'], true) - inputs: - azureSubscription: "JEG-Infrastructure" - scriptType: "bash" - scriptLocation: "scriptPath" - scriptPath: $(Build.SourcesDirectory)/scripts/image-annotation.sh - env: - ACR_NAME: msopenjdk - REGISTRIES: $(REGISTRIES) - task: AzureCLI@2 inputs: azureSubscription: "JEG-Infrastructure" scriptType: "bash" scriptLocation: "scriptPath" scriptPath: $(Build.SourcesDirectory)/scripts/build-image.sh - displayName: build image - env: - REGISTRY_TAGS: $(REGISTRIES) - IMAGE: $(image) - TAG: $(tag) - PACKAGE: $(package) - DISTRIBUTION: $(distro) - INSTALLER_IMAGE: $(installer_image) - INSTALLER_TAG: $(installer_tag) + ${{ if eq(parameters.dryrun, true) }}: + arguments: > + --image $(image) + --tag $(tag) + --package $(package) + --distribution $(distro) + --registries $(REGISTRIES) + --installer-image $(installer_image) + --installer-tag $(installer_tag) + --dryrun + ${{ else }}: + arguments: > + --image $(image) + --tag $(tag) + --package $(package) + --distribution $(distro) + --registries $(REGISTRIES) + --installer-image $(installer_image) + --installer-tag $(installer_tag) + displayName: build container image + + - template: /.devops/templates/annotate-image.yml@self + parameters: + registry: $(INTERNAL_ACR_REGISTRY) + organization: ${{ parameters.organization }} + feed: ${{ parameters.feed }} + package: ${{ parameters.az_package }} + dryrun: ${{ parameters.dryrun }} - stage: validate_and_publish displayName: "Validate & Publish" dependsOn: build_internal jobs: + - job: wait_for_validation displayName: wait for validation pool: server @@ -203,7 +209,6 @@ extends: onTimeout: "resume" - job: build_public - displayName: "build public " dependsOn: wait_for_validation pool: name: JEG-azurelinux-x64-release @@ -211,73 +216,110 @@ extends: strategy: matrix: ${{ parameters.jobs }} steps: - - task: AzureCLI@2 - displayName: "Download ORAS" - condition: ne( variables['new_LTS_image'], true) - inputs: - azureSubscription: "JEG-Infrastructure" - scriptType: "bash" - scriptLocation: "scriptPath" - scriptPath: $(Build.SourcesDirectory)/scripts/install-oras.sh - env: - AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) - ORAS_VERSION: 1.1.0 - ORGANIZATION: ${{ parameters.organization }} - FEED: ${{ parameters.feed }} - NAME: ${{ parameters.package }} + - template: /.devops/templates/prepare-annotation.yml@self + parameters: + registry: $(ACR_REGISTRY) + tag: $(version)-$(distro) - bash: | - REGISTRIES=msopenjdk.azurecr.io/public/openjdk/jdk:$(version)-$(distro) - TAGS="$(version)-$(distro)" + REGISTRIES=$(ACR_REGISTRY):$(version)-$(distro) if [[ "$(distro)" == "azurelinux" ]]; then - REGISTRIES+=";msopenjdk.azurecr.io/public/openjdk/jdk:$(version)-mariner" + REGISTRIES+=";$(ACR_REGISTRY):$(version)-mariner" fi echo "##vso[task.setvariable variable=REGISTRIES]$REGISTRIES" - echo "##vso[task.setvariable variable=TAGS]$TAGS" - displayName: Set environment variables + displayName: Set REGISTRIES variable - task: AzureCLI@2 - displayName: Annotate previous image - condition: ne( variables['new_LTS_image'], true) inputs: azureSubscription: "JEG-Infrastructure" scriptType: "bash" scriptLocation: "scriptPath" - scriptPath: $(Build.SourcesDirectory)/scripts/image-annotation.sh - env: - ACR_NAME: msopenjdk - REGISTRIES: $(REGISTRIES) + scriptPath: $(Build.SourcesDirectory)/scripts/build-image.sh + ${{ if eq(parameters.dryrun, true) }}: + arguments: > + --image $(image) + --tag $(tag) + --package $(package) + --distribution $(distro) + --registries $(REGISTRIES) + --installer-image $(installer_image) + --installer-tag $(installer_tag) + --dryrun + ${{ else }}: + arguments: > + --image $(image) + --tag $(tag) + --package $(package) + --distribution $(distro) + --registries $(REGISTRIES) + --installer-image $(installer_image) + --installer-tag $(installer_tag) + displayName: build container image - - task: AzureCLI@2 - inputs: - azureSubscription: "JEG-Infrastructure" - scriptType: "bash" - scriptLocation: "scriptPath" - scriptPath: scripts/build-image.sh - displayName: build image - env: - REGISTRY_TAGS: $(REGISTRIES) - IMAGE: $(image) - TAG: $(tag) - PACKAGE: $(package) - DISTRIBUTION: $(distro) - INSTALLER_IMAGE: $(installer_image) - INSTALLER_TAG: $(installer_tag) + - template: /.devops/templates/annotate-image.yml@self + parameters: + registry: $(ACR_REGISTRY) + organization: ${{ parameters.organization }} + feed: ${{ parameters.feed }} + package: ${{ parameters.az_package }} + dryrun: ${{ parameters.dryrun }} - - task: AzureCLI@2 - displayName: Trigger image signing - env: - AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) - inputs: - azureSubscription: "JEG-Infrastructure" - scriptType: "bash" - scriptLocation: "inlineScript" - inlineScript: | - az pipelines run \ - --branch main \ - --org ${{ parameters.organization }} \ - --project $(OPENJDK_PROJECT) \ - --id $(OPENJDK_SIGNING_ID) \ - --parameters openjdk_tags="[$(TAGS)]" \ - image_registry="msopenjdk.azurecr.io/public/openjdk" \ - image_name="jdk" + # - template: /.devops/templates/annotate-image.yml@self + # parameters: + # registry: $(INTERNAL_ACR_REGISTRY) + # tag: $(version)-$(distro) + # organization: ${{ parameters.organization }} + # feed: ${{ parameters.feed }} + # package: ${{ parameters.az_package }} + # dryrun: ${{ parameters.dryrun }} + + # - bash: | + # REGISTRIES=$(ACR_REGISTRY):$(version)-$(distro) + # TAGS="$(version)-$(distro)" + + # if [[ "$(distro)" == "azurelinux" ]]; then + # REGISTRIES+=";$(ACR_REGISTRY):$(version)-mariner" + # fi + + # echo "##vso[task.setvariable variable=REGISTRIES]$REGISTRIES" + # echo "##vso[task.setvariable variable=TAGS]$TAGS" + # displayName: Set environment variables + + # - task: AzureCLI@2 + # inputs: + # azureSubscription: "JEG-Infrastructure" + # scriptType: "bash" + # scriptLocation: "scriptPath" + # scriptPath: scripts/build-image.sh + # displayName: Build image + # env: + # REGISTRY_TAGS: $(REGISTRIES) + # IMAGE: $(image) + # TAG: $(tag) + # PACKAGE: $(package) + # DISTRIBUTION: $(distro) + # INSTALLER_IMAGE: $(installer_image) + # INSTALLER_TAG: $(installer_tag) + # ${{ if eq(parameters.dryrun, true) }}: + # DRYRUN: "true" + # ${{ else }}: + # DRYRUN: "false" + + - ${{ if ne(parameters.dryrun, true) }}: + - task: AzureCLI@2 + displayName: Trigger signing + env: + AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) + inputs: + azureSubscription: "JEG-Infrastructure" + scriptType: "bash" + scriptLocation: "inlineScript" + inlineScript: | + az pipelines run \ + --branch main \ + --org ${{ parameters.organization }} \ + --project $(OPENJDK_PROJECT) \ + --id $(OPENJDK_SIGNING_ID) \ + --parameters openjdk_tags="[$(TAGS)]" \ + image_registry="$(ACR_REGISTRY)" \ + image_name="jdk" diff --git a/.devops/templates/annotate-image.yml b/.devops/templates/annotate-image.yml new file mode 100644 index 0000000..68449cf --- /dev/null +++ b/.devops/templates/annotate-image.yml @@ -0,0 +1,64 @@ +parameters: + - name: registry + type: string + - name: organization + type: string + - name: feed + type: string + - name: package + type: string + - name: dryrun + type: boolean + default: false + +steps: + - task: AzureCLI@2 + displayName: Download ORAS + condition: eq(variables['hasExistingImages'], 'true') + inputs: + azureSubscription: "JEG-Infrastructure" + scriptType: "bash" + scriptLocation: "scriptPath" + scriptPath: $(Build.SourcesDirectory)/scripts/install-oras.sh + env: + AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) + ORAS_VERSION: 1.1.0 + ORGANIZATION: ${{ parameters.organization }} + FEED: ${{ parameters.feed }} + NAME: ${{ parameters.package }} + + - task: AzureCLI@2 + displayName: Annotate previous amd64 as EOL + condition: eq(variables['hasExistingImages'], 'true') + inputs: + azureSubscription: "JEG-Infrastructure" + scriptType: "bash" + scriptLocation: "scriptPath" + scriptPath: $(Build.SourcesDirectory)/scripts/image-annotation.sh + ${{ if eq(parameters.dryrun, true) }}: + arguments: > + -r ${{ parameters.registry }} + -m $(imageDigestAmd64) + -d + ${{ else }}: + arguments: > + -r ${{ parameters.registry }} + -m $(imageDigestAmd64) + + - task: AzureCLI@2 + displayName: Annotate previous arm64 as EOL + condition: eq(variables['hasExistingImages'], 'true') + inputs: + azureSubscription: "JEG-Infrastructure" + scriptType: "bash" + scriptLocation: "scriptPath" + scriptPath: $(Build.SourcesDirectory)/scripts/image-annotation.sh + ${{ if eq(parameters.dryrun, true) }}: + arguments: > + -r ${{ parameters.registry }} + -m $(imageDigestArm64) + -d + ${{ else }}: + arguments: > + -r ${{ parameters.registry }} + -m $(imageDigestArm64) \ No newline at end of file diff --git a/.devops/templates/prepare-annotation.yml b/.devops/templates/prepare-annotation.yml new file mode 100644 index 0000000..d3f92ec --- /dev/null +++ b/.devops/templates/prepare-annotation.yml @@ -0,0 +1,51 @@ +parameters: + - name: registry + type: string + - name: tag + type: string + +steps: + + - task: AzureCLI@2 + displayName: Gather container image manifests + condition: eq(variables['hasExistingImages'], 'true') + inputs: + azureSubscription: "JEG-Infrastructure" + scriptType: "bash" + scriptLocation: inlineScript + inlineScript: | + az acr login --name $(ACR_NAME) + manifest=$(az acr manifest show ${{ parameters.registry }}:${{ parameters.tag }} -o json | jq -c .) + echo "##vso[task.setvariable variable=manifest]$manifest" + + - task: PythonScript@0 + displayName: Output image digest variables + condition: eq(variables['hasExistingImages'], 'true') + env: + manifest: $(manifest) + inputs: + scriptSource: 'inline' + script: | + import json + import os + + amd64_digest = "" + arm64_digest = "" + manifest = json.loads(os.environ.get('manifest', {})) + + if not manifest: + print("##vso[task.logissue type=error]Container image manifest is empty or null. Unable to retrieve image digests!") + exit(1) + + for descriptor in manifest['manifests']: + if descriptor['platform']['architecture'] == 'amd64': + amd64_digest = descriptor['digest'] + elif descriptor['platform']['architecture'] == 'arm64': + arm64_digest = descriptor['digest'] + + # Theoretically, we should always have both, but log a warning in the event one is not found + if not amd64_digest or not arm64_digest: + print(f"##vso[task.logissue type=warning]Missing one of amd64_digest: {amd64_digest} or arm64_digest: {arm64_digest}.") + + print(f"##vso[task.setvariable variable=imageDigestAmd64]{amd64_digest}") + print(f"##vso[task.setvariable variable=imageDigestArm64]{arm64_digest}") diff --git a/scripts/build-image.sh b/scripts/build-image.sh index 16628dc..4d4f84c 100644 --- a/scripts/build-image.sh +++ b/scripts/build-image.sh @@ -1,18 +1,83 @@ #!/bin/bash + +dryRun=false + +while [[ "$#" -gt 0 ]]; do + case $1 in + -i | --image) + image="$2"; + shift 2 + ;; + -t | --tag) + tag="$2"; + shift 2 + ;; + -p | --package) + package="$2"; + shift 2 + ;; + -d | --distribution) + distro="$2"; + shift 2 + ;; + -r | --registries) + registryTags="$2"; + shift 2 + ;; + -D | --dryrun) + dryRun=true + shift + ;; + -I | --installer-image) + installerImg="$2"; + shift 2 + ;; + -T | --installer-tag) + installerTag="$2"; + shift 2 + ;; + *) echo "Unknown parameter passed: $1"; exit 1 ;; + esac +done + az acr login -n junipercontainerregistry -docker buildx create --name mybuilder --driver docker-container --driver-opt image=junipercontainerregistry.azurecr.io/mirror/moby/buildkit --platform linux/amd64,linux/arm64 --use +az acr login -n "$ACR_NAME" + +docker buildx create \ + --name mybuilder \ + --driver docker-container \ + --driver-opt image=junipercontainerregistry.azurecr.io/mirror/moby/buildkit \ + --platform linux/amd64,linux/arm64 \ + --use -az acr login -n msopenjdk -if [[ '$DISTRIBUTION' != 'distroless' ]]; then - BUILD_ARGS="--build-arg IMAGE=$IMAGE --build-arg TAG=$TAG --build-arg package=$PACKAGE" +if [[ "$distro" != "distroless" ]]; then + buildArgs="--build-arg IMAGE=$image --build-arg TAG=$tag --build-arg package=$package" else - BUILD_ARGS="--build-arg INSTALLER_IMAGE=$INSTALLER_IMAGE --build-arg INSTALLER_TAG=$INSTALLER_TAG --build-arg BASE_IMAGE=$(base_image) --build-arg BASE_TAG=$(base_tag) --build-arg package=$PACKAGE" + buildArgs="--build-arg INSTALLER_IMAGE=$installerImg --build-arg INSTALLER_TAG=$installerTag --build-arg BASE_IMAGE=$(base_image) --build-arg BASE_TAG=$(base_tag) --build-arg package=$package" fi -REGISTRY_TAGS="-t ${REGISTRY_TAGS/;/ -t }" +registryTags="-t ${registryTags/;/ -t }" # To push to a registry use --push # To build locally use --output=type=image,push=false -echo "docker buildx build --platform linux/amd64,linux/arm64 ${BUILD_ARGS} ${REGISTRY_TAGS} -f docker/$DISTRIBUTION/Dockerfile.$PACKAGE-jdk . --push" -docker buildx build --platform linux/amd64,linux/arm64 ${BUILD_ARGS} ${REGISTRY_TAGS} -f docker/$DISTRIBUTION/Dockerfile.$PACKAGE-jdk . --push \ No newline at end of file + +if [[ "$dryRun" == true ]]; then + echo "[DRY-RUN] Running in dry-run mode. No changes will be made." + echo "[DRY-RUN] Command that would be executed:" + echo "docker buildx build --platform linux/amd64,linux/arm64 ${buildArgs} ${registryTags} -f docker/$distro/Dockerfile.$package-jdk . --metadata-file metadata.json --push" +else + echo "docker buildx build --platform linux/amd64,linux/arm64 ${buildArgs} ${registryTags} -f docker/$distro/Dockerfile.$package-jdk . --push" + + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + ${buildArgs} \ + ${registryTags} \ + -f docker/$distro/Dockerfile.$package-jdk . \ + --metadata-file metadata.json \ + --push + + containerImageDigest=$(cat metadata.json | grep -oP '(?<="containerimage.digest": ")[^"]+') + echo "##vso[task.setvariable variable=containerImageDigest]$containerImageDigest" + rm metadata.json +fi \ No newline at end of file diff --git a/scripts/image-annotation.sh b/scripts/image-annotation.sh index 5e5a277..e63cdc4 100644 --- a/scripts/image-annotation.sh +++ b/scripts/image-annotation.sh @@ -1,36 +1,43 @@ #!/bin/bash -az acr login -n msopenjdk +az acr login -n "$ACR_NAME" if [[ $? -ne 0 ]]; then echo "Failed to login to ACR" exit 1 fi -IFS=';' read -ra REGISTRIES_ARRAY <<< "$REGISTRIES" +debug=false -for REGISTRY in "${REGISTRIES_ARRAY[@]}"; do - echo "Pulling... $REGISTRY" +while getopts "r:m:d" opt; do + case $opt in + r) registry="$OPTARG" ;; + m) manifest="$OPTARG" ;; + d) debug=true ;; + *) echo "Invalid option: -$OPTARG" ;; + esac +done - docker pull "$REGISTRY" - if [[ $? -ne 0 ]]; then - echo "Failed to pull image $REGISTRY" - exit 1 - fi +if [[ -z "$manifest" ]]; then + echo "##vso[task.logissue type=error]Container image manifest is empty or null. Unable to add annotation!" +fi - manifest=$(docker image inspect "$REGISTRY" | jq) - digest=$(echo $manifest | jq '.[0].RepoDigests[0]') - digest=${digest//\"/} - endOfLifeDate=$(date "+%Y-%m-%d") +endOfLifeDate=$(date "+%Y-%m-%d") - echo "Annotating image $digest with end-of-life date $endOfLifeDate" +echo "Annotating image ${registry}@${manifest} with end-of-life date ${endOfLifeDate}T00:00:00Z" + +if [[ "$debug" == true ]]; then + echo "[DRY-RUN] Running in dry-run mode. No changes will be made." + echo "[DRY-RUN] Command that would be executed:" + echo "oras attach --artifact-type \"application/vnd.microsoft.artifact.lifecycle\" --annotation \"vnd.microsoft.artifact.lifecycle.end-of-life.date=${endOfLifeDate}T00:00:00Z\" $registry@$manifest --verbose" +else oras attach \ --artifact-type "application/vnd.microsoft.artifact.lifecycle" \ --annotation "vnd.microsoft.artifact.lifecycle.end-of-life.date=${endOfLifeDate}T00:00:00Z" \ - $digest --verbose + $registry@$manifest \ + --verbose if [[ $? -ne 0 ]]; then echo "Failed to annotate image!" exit 1 fi - -done \ No newline at end of file +fi