From f8972c2e207d0f7888eb0798f0f4784be27ed287 Mon Sep 17 00:00:00 2001 From: Sushanta Das Date: Tue, 7 Jan 2025 13:27:59 +0530 Subject: [PATCH] Validate kustomize build in CI --- .github/workflows/check-kustomize-build.yaml | 13 + README.md | 9 + hack/build-manifests.sh | 66 ++ hack/get-kustomize.sh | 20 + hack/missing-ta-tasks.sh | 9 + hack/verify-manifests.sh | 21 + pipelines/core-services/core-services.yaml | 518 ++++++++++++ .../docker-build-multi-platform-oci-ta.yaml | 455 ++++++++++ .../docker-build-oci-ta.yaml | 442 ++++++++++ .../docker-build-rhtap.yaml | 285 +++++++ pipelines/docker-build/docker-build.yaml | 444 ++++++++++ pipelines/fbc-builder/fbc-builder.yaml | 261 ++++++ .../maven-zip-build-oci-ta.yaml | 245 ++++++ .../maven-zip-build/maven-zip-build.yaml | 250 ++++++ pipelines/rhtap/rhtap.yaml | 448 ++++++++++ .../tekton-bundle-builder.yaml | 269 ++++++ pipelines/template-build/template-build.yaml | 784 +++++++++--------- .../0.1/build-image-index.yaml | 111 +-- .../0.1/build-image-manifest.yaml | 222 +++++ .../0.1/kustomization.yaml | 5 - task/build-maven-zip/0.1/build-maven-zip.yaml | 113 +-- task/buildah-10gb/0.1/buildah-10gb.yaml | 585 +++++++++++++ task/buildah-10gb/0.2/buildah-10gb.yaml | 742 +++++++++++++++++ task/buildah-20gb/0.1/buildah-20gb.yaml | 585 +++++++++++++ task/buildah-20gb/0.2/buildah-20gb.yaml | 742 +++++++++++++++++ task/buildah-24gb/0.1/buildah-24gb.yaml | 586 +++++++++++++ task/buildah-24gb/0.2/buildah-24gb.yaml | 742 +++++++++++++++++ task/buildah-6gb/0.1/buildah-6gb.yaml | 585 +++++++++++++ task/buildah-6gb/0.2/buildah-6gb.yaml | 742 +++++++++++++++++ task/buildah-8gb/0.1/buildah-8gb.yaml | 585 +++++++++++++ task/buildah-8gb/0.2/buildah-8gb.yaml | 742 +++++++++++++++++ task/buildah-min/0.1/buildah-min.yaml | 582 +++++++++++++ task/buildah-min/0.2/buildah-min.yaml | 738 +++++++++++++++++ task/buildah-oci-ta/0.1/kustomization.yaml | 5 - task/buildah/0.1/buildah.yaml | 251 +++--- task/buildah/0.2/buildah.yaml | 289 +++---- task/buildah/0.3/buildah.yaml | 267 +++--- .../0.1/fbc-related-image-check.yaml | 121 +-- .../0.2/fbc-related-image-check.yaml | 75 ++ task/fbc-validation/0.1/fbc-validation.yaml | 468 +++++------ task/fbc-validation/0.2/fbc-validation.yaml | 282 +++++++ task/inspect-image/0.1/inspect-image.yaml | 77 +- task/inspect-image/0.2/inspect-image.yaml | 168 ++++ .../0.1/verify-enterprise-contract.yaml | 353 ++++---- 44 files changed, 13874 insertions(+), 1428 deletions(-) create mode 100644 .github/workflows/check-kustomize-build.yaml create mode 100755 hack/build-manifests.sh create mode 100755 hack/get-kustomize.sh create mode 100755 hack/verify-manifests.sh create mode 100644 pipelines/core-services/core-services.yaml create mode 100644 pipelines/docker-build-multi-platform-oci-ta/docker-build-multi-platform-oci-ta.yaml create mode 100644 pipelines/docker-build-oci-ta/docker-build-oci-ta.yaml create mode 100644 pipelines/docker-build-rhtap/docker-build-rhtap.yaml create mode 100644 pipelines/docker-build/docker-build.yaml create mode 100644 pipelines/fbc-builder/fbc-builder.yaml create mode 100644 pipelines/maven-zip-build-oci-ta/maven-zip-build-oci-ta.yaml create mode 100644 pipelines/maven-zip-build/maven-zip-build.yaml create mode 100644 pipelines/rhtap/rhtap.yaml create mode 100644 pipelines/tekton-bundle-builder/tekton-bundle-builder.yaml create mode 100644 task/build-image-manifest/0.1/build-image-manifest.yaml delete mode 100644 task/build-maven-zip-oci-ta/0.1/kustomization.yaml create mode 100644 task/buildah-10gb/0.1/buildah-10gb.yaml create mode 100644 task/buildah-10gb/0.2/buildah-10gb.yaml create mode 100644 task/buildah-20gb/0.1/buildah-20gb.yaml create mode 100644 task/buildah-20gb/0.2/buildah-20gb.yaml create mode 100644 task/buildah-24gb/0.1/buildah-24gb.yaml create mode 100644 task/buildah-24gb/0.2/buildah-24gb.yaml create mode 100644 task/buildah-6gb/0.1/buildah-6gb.yaml create mode 100644 task/buildah-6gb/0.2/buildah-6gb.yaml create mode 100644 task/buildah-8gb/0.1/buildah-8gb.yaml create mode 100644 task/buildah-8gb/0.2/buildah-8gb.yaml create mode 100644 task/buildah-min/0.1/buildah-min.yaml create mode 100644 task/buildah-min/0.2/buildah-min.yaml delete mode 100644 task/buildah-oci-ta/0.1/kustomization.yaml create mode 100644 task/fbc-related-image-check/0.2/fbc-related-image-check.yaml create mode 100644 task/fbc-validation/0.2/fbc-validation.yaml create mode 100644 task/inspect-image/0.2/inspect-image.yaml diff --git a/.github/workflows/check-kustomize-build.yaml b/.github/workflows/check-kustomize-build.yaml new file mode 100644 index 0000000000..01ef0b40f5 --- /dev/null +++ b/.github/workflows/check-kustomize-build.yaml @@ -0,0 +1,13 @@ +name: Validate PR - kustomize manifests +'on': + pull_request: + branches: [main] +jobs: + kustomize-build: + name: Check Kustomize Build of Task and Pipelines + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + - name: Validate Manifests + run: hack/verify-manifests.sh diff --git a/README.md b/README.md index f90a979965..9451d7aa64 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,15 @@ Buildah also has a remote version, which can be generated with: ./hack/generate-buildah-remote.sh ``` +## Making changes to tasks and pipelines + +If your tasks or pipelines contains `kustomization.yaml`, after making changes to the tasks or pipelines, run `hack/build-manifests.sh` and +commit the generated manifests as well to the same directory (in addition to your changes). +It will help us to make sure the kustomize build is successful and review the changes. + +`hack/build-manifests.sh` needs `kustomize` to be installed locally.It can be downloaded using `hack/get-kustomize.sh` script. + + ## Testing ### Prerequisites diff --git a/hack/build-manifests.sh b/hack/build-manifests.sh new file mode 100755 index 0000000000..2161bfc62e --- /dev/null +++ b/hack/build-manifests.sh @@ -0,0 +1,66 @@ +#!/bin/bash -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" + +# You can ignore building manifests for some tasks by providing the SKIP_TASKS variable +# with the task name separated by a space, for example: +# SKIP_TASKS="git-clone init" + +SKIP_TASKS="generate-odcs-compose provision-env-with-ephemeral-namespace verify-signed-rpms" + +# You can ignore building manifests for some pipelines by providing the SKIP_PIPELINES variable +# with the task name separated by a space, for example: +# SKIP_PIPELINES="rhtap gitops-pull-request-rhtap" + +SKIP_PIPELINES="gitops-pull-request-rhtap" + +main() { + local kustomize=${1:-kustomize} + local dirs + + cd "$SCRIPT_DIR/.." + task_dirs=$(find task -maxdepth 4 \( -name 'kustomization.yaml' -o -name 'kustomization.yml' \) -exec dirname {} \;) + pipeline_dirs=$(find pipelines -mindepth 2 \( -name 'kustomization.yaml' -o -name 'kustomization.yml' \) -exec dirname {} \;) + local ret=0 + + for task_dir in ${task_dirs}; do + echo "Building task directory: ${task_dir}" + task_name=$(echo $task_dir | awk -F '/' '{print $2}') + # Skip the tasks mentioned in SKIP_TASKS + skipit= + for tname in ${SKIP_TASKS};do + [[ ${tname} == "${task_name}" ]] && skipit=True + done + [[ -n ${skipit} ]] && continue + if ! "$kustomize" build -o "$task_dir/$task_name.yaml" "$task_dir"; then + echo "failed to build task: $task_dir" >&2 + ret=1 + fi + done + + for pipeline_dir in ${pipeline_dirs}; do + echo "Building pipeline directory: ${pipeline_dir}" + pipeline_name=$(echo $pipeline_dir | awk -F '/' '{print $2}') + # Skip the pipelines mentioned in SKIP_PIPELINES + skipit= + for pname in ${SKIP_PIPELINES};do + [[ ${pname} == "${pipeline_name}" ]] && skipit=True + done + [[ -n ${skipit} ]] && continue + + if ! "$kustomize" build -o "$pipeline_dir/$pipeline_name.yaml" "$pipeline_dir"; then + echo "failed to build pipeline: $pipeline_dir" >&2 + ret=1 + fi + done + + exit "$ret" + +} + +if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then + main "$@" +fi + + + diff --git a/hack/get-kustomize.sh b/hack/get-kustomize.sh new file mode 100755 index 0000000000..281165b8ee --- /dev/null +++ b/hack/get-kustomize.sh @@ -0,0 +1,20 @@ +#!/bin/bash -e + +main() { + local target_dir=${1:?target dir for storing kustomize should be specified} + + mkdir -p "$target_dir" + if [[ -f "${target_dir}/kustomize" ]]; then + exit 0 + fi + + curl -L https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv5.3.0/kustomize_v5.3.0_linux_amd64.tar.gz | tar -C "$target_dir" -xvzf - + + ls -l "$target_dir" + echo "PATH -> $PATH" +} + + +if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then + main "$@" +fi diff --git a/hack/missing-ta-tasks.sh b/hack/missing-ta-tasks.sh index 532000ea81..8bf90116f9 100755 --- a/hack/missing-ta-tasks.sh +++ b/hack/missing-ta-tasks.sh @@ -14,22 +14,31 @@ trap 'rm "${tmp_files[@]}" > /dev/null 2>&1' EXIT # Tasks that are currently missing Trusted Artifact variant todo=( task/buildah-10gb/0.2/kustomization.yaml + task/buildah-10gb/0.2/buildah-10gb.yaml task/buildah-20gb/0.2/kustomization.yaml + task/buildah-20gb/0.2/buildah-20gb.yaml task/buildah-24gb/0.2/kustomization.yaml + task/buildah-24gb/0.2/buildah-24gb.yaml task/buildah-6gb/0.2/kustomization.yaml + task/buildah-6gb/0.2/buildah-6gb.yaml task/buildah-8gb/0.2/kustomization.yaml + task/buildah-8gb/0.2/buildah-8gb.yaml task/buildah-min/0.2/kustomization.yaml + task/buildah-min/0.2/buildah-min.yaml task/buildah-rhtap/0.1/buildah-rhtap.yaml task/download-sbom-from-url-in-attestation/0.1/download-sbom-from-url-in-attestation.yaml task/fbc-related-image-check/0.1/fbc-related-image-check.yaml task/fbc-related-image-check/0.2/kustomization.yaml + task/fbc-related-image-check/0.2/fbc-related-image-check.yaml task/fbc-validation/0.1/fbc-validation.yaml task/fbc-validation/0.2/kustomization.yaml + task/fbc-validation/0.2/fbc-validation.yaml task/gather-deploy-images/0.1/gather-deploy-images.yaml task/generate-odcs-compose/0.2/generate-odcs-compose.yaml task/generate-odcs-compose/0.2/kustomization.yaml task/inspect-image/0.1/inspect-image.yaml task/inspect-image/0.2/kustomization.yaml + task/inspect-image/0.2/inspect-image.yaml task/operator-sdk-generate-bundle/0.1/operator-sdk-generate-bundle.yaml task/opm-get-bundle-version/0.1/opm-get-bundle-version.yaml task/opm-render-bundles/0.1/opm-render-bundles.yaml diff --git a/hack/verify-manifests.sh b/hack/verify-manifests.sh new file mode 100755 index 0000000000..d4552a7629 --- /dev/null +++ b/hack/verify-manifests.sh @@ -0,0 +1,21 @@ +#!/bin/bash -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" + +main() { + local kustomize=${1:-kustomize} + + "${SCRIPT_DIR}"/build-manifests.sh "$kustomize" + if [[ $(git status --porcelain) ]]; then + git diff --exit-code >&2 || { + echo "Did you forget to build the manifests locally?" >&2; + echo "Please run ./hack/build-manifests.sh and update your PR" >&2; + exit 1; + } + fi + echo "changes are up to date" >&2 +} + +if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then + main "$@" +fi diff --git a/pipelines/core-services/core-services.yaml b/pipelines/core-services/core-services.yaml new file mode 100644 index 0000000000..192ee15288 --- /dev/null +++ b/pipelines/core-services/core-services.yaml @@ -0,0 +1,518 @@ +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + annotations: + appstudio.openshift.io/core-services-ci: "1" + labels: + pipelines.openshift.io/runtime: generic + pipelines.openshift.io/strategy: docker + pipelines.openshift.io/used-by: build-cloud + name: docker-build +spec: + description: | + This pipeline is ideal for building container images from a Containerfile while reducing network traffic. + + _Uses `buildah` to create a container image. It also optionally creates a source image and runs some build-time tests. EC will flag a violation for [`trusted_task.trusted`](https://enterprisecontract.dev/docs/ec-policies/release_policy.html#trusted_task__trusted) if any tasks are added to the pipeline. + This pipeline is pushed as a Tekton bundle to [quay.io](https://quay.io/repository/konflux-ci/tekton-catalog/pipeline-docker-build?tab=tags)_ + finally: + - name: show-sbom + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + taskRef: + name: show-sbom + version: "0.1" + - name: show-summary + params: + - name: pipelinerun-name + value: $(context.pipelineRun.name) + - name: git-url + value: $(tasks.clone-repository.results.url)?rev=$(tasks.clone-repository.results.commit) + - name: image-url + value: $(params.output-image) + - name: build-task-status + value: $(tasks.build-image-index.status) + taskRef: + name: summary + version: "0.2" + workspaces: + - name: workspace + workspace: workspace + - name: infra-deployments-mr + params: + - name: ORIGIN_REPO + value: $(params.git-url) + - name: REVISION + value: $(params.revision) + - name: SCRIPT + value: $(params.infra-deployment-update-script) + taskRef: + name: update-infra-deployments + version: "0.1" + when: + - input: $(params.infra-deployment-update-script) + operator: notin + values: + - "" + - input: $(tasks.status) + operator: notin + values: + - Failed + - name: update-repo + params: + - name: ORIGIN_REPO + value: $(params.git-url) + - name: REVISION + value: $(params.revision) + - name: SCRIPT + value: $(params.update-repo-script) + - name: TARGET_GH_REPO + value: $(params.update-repo-name) + taskRef: + name: update-infra-deployments + version: "0.1" + when: + - input: $(params.update-repo-script) + operator: notin + values: + - "" + - input: $(params.update-repo-name) + operator: notin + values: + - "" + - input: $(tasks.status) + operator: notin + values: + - Failed + - name: slack-webhook-notification + params: + - name: message + value: Tekton pipelineRun $(context.pipelineRun.name) failed + - name: key-name + value: $(params.slack-webhook-notification-team) + taskRef: + name: slack-webhook-notification + version: "0.1" + when: + - input: $(params.slack-webhook-notification-team) + operator: notin + values: + - "" + - input: $(tasks.status) + operator: in + values: + - Failed + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: . + description: Path to the source code of an application's component from where + to build image. + name: path-context + type: string + - default: Dockerfile + description: Path to the Dockerfile inside the context specified by parameter + path-context + name: dockerfile + type: string + - default: "false" + description: Force rebuild image + name: rebuild + type: string + - default: "false" + description: Skip checks against built image + name: skip-checks + type: string + - default: "false" + description: Execute the build with network isolation + name: hermetic + type: string + - default: "" + description: Build dependencies to be prefetched by Cachi2 + name: prefetch-input + type: string + - default: "" + description: Image tag expiration time, time values could be something like 1h, + 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + - default: "false" + description: Build a source image. + name: build-source-image + type: string + - default: "false" + description: Add built image into an OCI image index + name: build-image-index + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) for buildah + name: build-args + type: array + - default: "" + description: Path to a file with build arguments for buildah, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: build-args-file + type: string + - default: "" + name: infra-deployment-update-script + - default: "" + name: update-repo-script + - default: "" + name: update-repo-name + - default: "" + name: slack-webhook-notification-team + results: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + tasks: + - name: init + params: + - name: image-url + value: $(params.output-image) + - name: rebuild + value: $(params.rebuild) + - name: skip-checks + value: $(params.skip-checks) + taskRef: + name: init + version: "0.2" + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + runAfter: + - init + taskRef: + name: git-clone + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: output + workspace: workspace + - name: basic-auth + workspace: git-auth + - name: prefetch-dependencies + params: + - name: input + value: $(params.prefetch-input) + runAfter: + - clone-repository + taskRef: + name: prefetch-dependencies + version: "0.1" + when: + - input: $(params.prefetch-input) + operator: notin + values: + - "" + workspaces: + - name: source + workspace: workspace + - name: git-basic-auth + workspace: git-auth + - name: netrc + workspace: netrc + - name: build-container + params: + - name: IMAGE + value: $(params.output-image) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: HERMETIC + value: $(params.hermetic) + - name: PREFETCH_INPUT + value: $(params.prefetch-input) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: BUILD_ARGS + value: + - $(params.build-args[*]) + - name: BUILD_ARGS_FILE + value: $(params.build-args-file) + runAfter: + - prefetch-dependencies + taskRef: + name: buildah + version: "0.3" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: source + workspace: workspace + - name: build-image-index + params: + - name: IMAGE + value: $(params.output-image) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: ALWAYS_BUILD_INDEX + value: $(params.build-image-index) + - name: IMAGES + value: + - $(tasks.build-container.results.IMAGE_URL)@$(tasks.build-container.results.IMAGE_DIGEST) + runAfter: + - build-container + taskRef: + name: build-image-index + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + - name: build-source-image + params: + - name: BINARY_IMAGE + value: $(params.output-image) + runAfter: + - build-image-index + taskRef: + name: source-build + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + - input: $(params.build-source-image) + operator: in + values: + - "true" + workspaces: + - name: workspace + workspace: workspace + - name: deprecated-base-image-check + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: deprecated-image-check + version: "0.4" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: clair-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: clair-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: ecosystem-cert-preflight-checks + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: ecosystem-cert-preflight-checks + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-snyk-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: sast-snyk-check + version: "0.3" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: clamav-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: clamav-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-coverity-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - coverity-availability-check + taskRef: + name: sast-coverity-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - input: $(tasks.coverity-availability-check.results.STATUS) + operator: in + values: + - success + workspaces: + - name: workspace + workspace: workspace + - name: coverity-availability-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: coverity-availability-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: sast-shell-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: sast-shell-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: sast-unicode-check + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: sast-unicode-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: apply-tags + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: apply-tags + version: "0.1" + - name: push-dockerfile + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + runAfter: + - build-image-index + taskRef: + name: push-dockerfile + version: "0.1" + workspaces: + - name: workspace + workspace: workspace + - name: rpms-signature-scan + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: rpms-signature-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + - name: git-auth + optional: true + - name: netrc + optional: true diff --git a/pipelines/docker-build-multi-platform-oci-ta/docker-build-multi-platform-oci-ta.yaml b/pipelines/docker-build-multi-platform-oci-ta/docker-build-multi-platform-oci-ta.yaml new file mode 100644 index 0000000000..eba8824056 --- /dev/null +++ b/pipelines/docker-build-multi-platform-oci-ta/docker-build-multi-platform-oci-ta.yaml @@ -0,0 +1,455 @@ +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + labels: + pipelines.openshift.io/runtime: generic + pipelines.openshift.io/strategy: docker + pipelines.openshift.io/used-by: build-cloud + name: docker-build-multi-platform-oci-ta +spec: + description: | + This pipeline is ideal for building multi-arch container images from a Containerfile while maintaining trust after pipeline customization. + + _Uses `buildah` to create a multi-platform container image leveraging [trusted artifacts](https://konflux-ci.dev/architecture/ADR/0036-trusted-artifacts.html). It also optionally creates a source image and runs some build-time tests. This pipeline requires that the [multi platform controller](https://github.com/konflux-ci/multi-platform-controller) is deployed and configured on your Konflux instance. Information is shared between tasks using OCI artifacts instead of PVCs. EC will pass the [`trusted_task.trusted`](https://enterprisecontract.dev/docs/ec-policies/release_policy.html#trusted_task__trusted) policy as long as all data used to build the artifact is generated from trusted tasks. + This pipeline is pushed as a Tekton bundle to [quay.io](https://quay.io/repository/konflux-ci/tekton-catalog/pipeline-docker-build-multi-platform-oci-ta?tab=tags)_ + finally: + - name: show-sbom + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + taskRef: + name: show-sbom + version: "0.1" + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: . + description: Path to the source code of an application's component from where + to build image. + name: path-context + type: string + - default: Dockerfile + description: Path to the Dockerfile inside the context specified by parameter + path-context + name: dockerfile + type: string + - default: "false" + description: Force rebuild image + name: rebuild + type: string + - default: "false" + description: Skip checks against built image + name: skip-checks + type: string + - default: "false" + description: Execute the build with network isolation + name: hermetic + type: string + - default: "" + description: Build dependencies to be prefetched by Cachi2 + name: prefetch-input + type: string + - default: "" + description: Image tag expiration time, time values could be something like 1h, + 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + - default: "false" + description: Build a source image. + name: build-source-image + type: string + - default: "true" + description: Add built image into an OCI image index + name: build-image-index + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) for buildah + name: build-args + type: array + - default: "" + description: Path to a file with build arguments for buildah, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: build-args-file + type: string + - default: + - linux/x86_64 + description: List of platforms to build the container images on. The available + set of values is determined by the configuration of the multi-platform-controller. + name: build-platforms + type: array + results: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + tasks: + - name: init + params: + - name: image-url + value: $(params.output-image) + - name: rebuild + value: $(params.rebuild) + - name: skip-checks + value: $(params.skip-checks) + taskRef: + name: init + version: "0.2" + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + - name: ociStorage + value: $(params.output-image).git + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + runAfter: + - init + taskRef: + name: git-clone-oci-ta + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: basic-auth + workspace: git-auth + - name: prefetch-dependencies + params: + - name: input + value: $(params.prefetch-input) + - name: SOURCE_ARTIFACT + value: $(tasks.clone-repository.results.SOURCE_ARTIFACT) + - name: ociStorage + value: $(params.output-image).prefetch + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + runAfter: + - clone-repository + taskRef: + name: prefetch-dependencies-oci-ta + version: "0.1" + workspaces: + - name: git-basic-auth + workspace: git-auth + - name: netrc + workspace: netrc + - matrix: + params: + - name: PLATFORM + value: + - $(params.build-platforms) + name: build-images + params: + - name: IMAGE + value: $(params.output-image) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: HERMETIC + value: $(params.hermetic) + - name: PREFETCH_INPUT + value: $(params.prefetch-input) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: BUILD_ARGS + value: + - $(params.build-args[*]) + - name: BUILD_ARGS_FILE + value: $(params.build-args-file) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: IMAGE_APPEND_PLATFORM + value: "true" + runAfter: + - prefetch-dependencies + taskRef: + name: buildah-remote-oci-ta + version: "0.3" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: [] + - name: build-image-index + params: + - name: IMAGE + value: $(params.output-image) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: ALWAYS_BUILD_INDEX + value: $(params.build-image-index) + - name: IMAGES + value: + - $(tasks.build-images.results.IMAGE_REF[*]) + runAfter: + - build-images + taskRef: + name: build-image-index + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + - name: build-source-image + params: + - name: BINARY_IMAGE + value: $(params.output-image) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-image-index + taskRef: + name: source-build-oci-ta + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + - input: $(params.build-source-image) + operator: in + values: + - "true" + workspaces: [] + - name: deprecated-base-image-check + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: deprecated-image-check + version: "0.4" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: clair-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: clair-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: ecosystem-cert-preflight-checks + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: ecosystem-cert-preflight-checks + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-snyk-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-image-index + taskRef: + name: sast-snyk-check-oci-ta + version: "0.3" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: [] + - name: clamav-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: clamav-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-coverity-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - coverity-availability-check + taskRef: + name: sast-coverity-check-oci-ta + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - input: $(tasks.coverity-availability-check.results.STATUS) + operator: in + values: + - success + workspaces: [] + - name: coverity-availability-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-image-index + taskRef: + name: coverity-availability-check-oci-ta + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: [] + - name: sast-shell-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-image-index + taskRef: + name: sast-shell-check-oci-ta + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: [] + - name: sast-unicode-check + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-image-index + taskRef: + name: sast-shell-check-oci-ta + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: [] + - name: apply-tags + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: apply-tags + version: "0.1" + - name: push-dockerfile + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + runAfter: + - build-image-index + taskRef: + name: push-dockerfile-oci-ta + version: "0.1" + workspaces: [] + - name: rpms-signature-scan + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: rpms-signature-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: git-auth + optional: true + - name: netrc + optional: true diff --git a/pipelines/docker-build-oci-ta/docker-build-oci-ta.yaml b/pipelines/docker-build-oci-ta/docker-build-oci-ta.yaml new file mode 100644 index 0000000000..1045bc82a9 --- /dev/null +++ b/pipelines/docker-build-oci-ta/docker-build-oci-ta.yaml @@ -0,0 +1,442 @@ +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + labels: + pipelines.openshift.io/runtime: generic + pipelines.openshift.io/strategy: docker + pipelines.openshift.io/used-by: build-cloud + name: docker-build-oci-ta +spec: + description: | + This pipeline is ideal for building container images from a Containerfile while maintaining trust after pipeline customization. + + _Uses `buildah` to create a container image leveraging [trusted artifacts](https://konflux-ci.dev/architecture/ADR/0036-trusted-artifacts.html). It also optionally creates a source image and runs some build-time tests. Information is shared between tasks using OCI artifacts instead of PVCs. EC will pass the [`trusted_task.trusted`](https://enterprisecontract.dev/docs/ec-policies/release_policy.html#trusted_task__trusted) policy as long as all data used to build the artifact is generated from trusted tasks. + This pipeline is pushed as a Tekton bundle to [quay.io](https://quay.io/repository/konflux-ci/tekton-catalog/pipeline-docker-build-oci-ta?tab=tags)_ + finally: + - name: show-sbom + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + taskRef: + name: show-sbom + version: "0.1" + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: . + description: Path to the source code of an application's component from where + to build image. + name: path-context + type: string + - default: Dockerfile + description: Path to the Dockerfile inside the context specified by parameter + path-context + name: dockerfile + type: string + - default: "false" + description: Force rebuild image + name: rebuild + type: string + - default: "false" + description: Skip checks against built image + name: skip-checks + type: string + - default: "false" + description: Execute the build with network isolation + name: hermetic + type: string + - default: "" + description: Build dependencies to be prefetched by Cachi2 + name: prefetch-input + type: string + - default: "" + description: Image tag expiration time, time values could be something like 1h, + 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + - default: "false" + description: Build a source image. + name: build-source-image + type: string + - default: "false" + description: Add built image into an OCI image index + name: build-image-index + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) for buildah + name: build-args + type: array + - default: "" + description: Path to a file with build arguments for buildah, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: build-args-file + type: string + results: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + tasks: + - name: init + params: + - name: image-url + value: $(params.output-image) + - name: rebuild + value: $(params.rebuild) + - name: skip-checks + value: $(params.skip-checks) + taskRef: + name: init + version: "0.2" + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + - name: ociStorage + value: $(params.output-image).git + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + runAfter: + - init + taskRef: + name: git-clone-oci-ta + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: basic-auth + workspace: git-auth + - name: prefetch-dependencies + params: + - name: input + value: $(params.prefetch-input) + - name: SOURCE_ARTIFACT + value: $(tasks.clone-repository.results.SOURCE_ARTIFACT) + - name: ociStorage + value: $(params.output-image).prefetch + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + runAfter: + - clone-repository + taskRef: + name: prefetch-dependencies-oci-ta + version: "0.1" + workspaces: + - name: git-basic-auth + workspace: git-auth + - name: netrc + workspace: netrc + - name: build-container + params: + - name: IMAGE + value: $(params.output-image) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: HERMETIC + value: $(params.hermetic) + - name: PREFETCH_INPUT + value: $(params.prefetch-input) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: BUILD_ARGS + value: + - $(params.build-args[*]) + - name: BUILD_ARGS_FILE + value: $(params.build-args-file) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - prefetch-dependencies + taskRef: + name: buildah-oci-ta + version: "0.3" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: [] + - name: build-image-index + params: + - name: IMAGE + value: $(params.output-image) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: ALWAYS_BUILD_INDEX + value: $(params.build-image-index) + - name: IMAGES + value: + - $(tasks.build-container.results.IMAGE_URL)@$(tasks.build-container.results.IMAGE_DIGEST) + runAfter: + - build-container + taskRef: + name: build-image-index + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + - name: build-source-image + params: + - name: BINARY_IMAGE + value: $(params.output-image) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-image-index + taskRef: + name: source-build-oci-ta + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + - input: $(params.build-source-image) + operator: in + values: + - "true" + workspaces: [] + - name: deprecated-base-image-check + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: deprecated-image-check + version: "0.4" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: clair-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: clair-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: ecosystem-cert-preflight-checks + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: ecosystem-cert-preflight-checks + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-snyk-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-image-index + taskRef: + name: sast-snyk-check-oci-ta + version: "0.3" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: [] + - name: clamav-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: clamav-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-coverity-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - coverity-availability-check + taskRef: + name: sast-coverity-check-oci-ta + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - input: $(tasks.coverity-availability-check.results.STATUS) + operator: in + values: + - success + workspaces: [] + - name: coverity-availability-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-image-index + taskRef: + name: coverity-availability-check-oci-ta + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: [] + - name: sast-shell-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-image-index + taskRef: + name: sast-shell-check-oci-ta + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: [] + - name: sast-unicode-check + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-image-index + taskRef: + name: sast-shell-check-oci-ta + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: [] + - name: apply-tags + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: apply-tags + version: "0.1" + - name: push-dockerfile + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + runAfter: + - build-image-index + taskRef: + name: push-dockerfile-oci-ta + version: "0.1" + workspaces: [] + - name: rpms-signature-scan + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: rpms-signature-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: git-auth + optional: true + - name: netrc + optional: true diff --git a/pipelines/docker-build-rhtap/docker-build-rhtap.yaml b/pipelines/docker-build-rhtap/docker-build-rhtap.yaml new file mode 100644 index 0000000000..28ba88e58f --- /dev/null +++ b/pipelines/docker-build-rhtap/docker-build-rhtap.yaml @@ -0,0 +1,285 @@ +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + labels: + pipelines.openshift.io/runtime: generic + pipelines.openshift.io/strategy: docker + pipelines.openshift.io/used-by: build-cloud + name: docker-build-rhtap +spec: + finally: + - name: show-sbom + params: + - name: IMAGE_URL + value: $(tasks.build-container.results.IMAGE_URL) + taskRef: + name: show-sbom-rhdh + version: "0.1" + - name: show-summary + params: + - name: pipelinerun-name + value: $(context.pipelineRun.name) + - name: git-url + value: $(tasks.clone-repository.results.url)?rev=$(tasks.clone-repository.results.commit) + - name: image-url + value: $(params.output-image) + - name: build-task-status + value: $(tasks.build-container.status) + taskRef: + name: summary + version: "0.2" + workspaces: + - name: workspace + workspace: workspace + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: . + description: Path to the source code of an application's component from where + to build image. + name: path-context + type: string + - default: Dockerfile + description: Path to the Dockerfile inside the context specified by parameter + path-context + name: dockerfile + type: string + - default: "false" + description: Force rebuild image + name: rebuild + type: string + - default: "" + description: Image tag expiration time, time values could be something like 1h, + 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + - default: rox-api-token + name: stackrox-secret + type: string + - default: gitops-auth-secret + description: 'Secret name to enable this pipeline to update the gitops repo with + the new image. ' + name: gitops-auth-secret-name + type: string + - default: push + description: Event that triggered the pipeline run, e.g. push, pull_request + name: event-type + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) for buildah + name: build-args + type: array + - default: "" + description: Path to a file with build arguments for buildah, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: build-args-file + type: string + results: + - name: IMAGE_URL + value: $(tasks.build-container.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-container.results.IMAGE_DIGEST) + - name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + - name: ACS_SCAN_OUTPUT + value: $(tasks.acs-image-scan.results.SCAN_OUTPUT) + tasks: + - name: init + params: + - name: image-url + value: $(params.output-image) + - name: rebuild + value: $(params.rebuild) + taskRef: + name: init + version: "0.2" + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + runAfter: + - init + taskRef: + name: git-clone + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: output + workspace: workspace + - name: basic-auth + workspace: git-auth + - name: build-container + params: + - name: IMAGE + value: $(params.output-image) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: BUILD_ARGS + value: + - $(params.build-args[*]) + - name: BUILD_ARGS_FILE + value: $(params.build-args-file) + runAfter: + - clone-repository + taskRef: + name: buildah-rhtap + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: source + workspace: workspace + - name: sast-unicode-check + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: sast-unicode-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: apply-tags + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: apply-tags + version: "0.1" + - name: push-dockerfile + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + runAfter: + - build-image-index + taskRef: + name: push-dockerfile + version: "0.1" + workspaces: + - name: workspace + workspace: workspace + - name: rpms-signature-scan + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: rpms-signature-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: acs-image-check + params: + - name: rox-secret-name + value: $(params.stackrox-secret) + - name: image + value: $(params.output-image) + - name: insecure-skip-tls-verify + value: "true" + - name: image-digest + value: $(tasks.build-container.results.IMAGE_DIGEST) + runAfter: + - build-container + taskRef: + name: acs-image-check + - name: acs-image-scan + params: + - name: rox-secret-name + value: $(params.stackrox-secret) + - name: image + value: $(params.output-image) + - name: insecure-skip-tls-verify + value: "true" + - name: image-digest + value: $(tasks.build-container.results.IMAGE_DIGEST) + runAfter: + - build-container + taskRef: + kind: Task + name: acs-image-scan + - name: acs-deploy-check + params: + - name: rox-secret-name + value: $(params.stackrox-secret) + - name: gitops-repo-url + value: $(params.git-url)-gitops + - name: insecure-skip-tls-verify + value: "true" + runAfter: + - update-deployment + taskRef: + kind: Task + name: acs-deploy-check + when: + - input: $(params.event-type) + operator: in + values: + - push + - Push + - name: update-deployment + params: + - name: gitops-repo-url + value: $(params.git-url)-gitops + - name: image + value: $(tasks.build-container.results.IMAGE_URL)@$(tasks.build-container.results.IMAGE_DIGEST) + - name: gitops-auth-secret-name + value: $(params.gitops-auth-secret-name) + runAfter: + - build-container + taskRef: + kind: Task + name: update-deployment + when: + - input: $(params.event-type) + operator: notin + values: + - pull_request + - Merge Request + workspaces: + - name: workspace + - name: git-auth + optional: true diff --git a/pipelines/docker-build/docker-build.yaml b/pipelines/docker-build/docker-build.yaml new file mode 100644 index 0000000000..93ec9aabe4 --- /dev/null +++ b/pipelines/docker-build/docker-build.yaml @@ -0,0 +1,444 @@ +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + labels: + pipelines.openshift.io/runtime: generic + pipelines.openshift.io/strategy: docker + pipelines.openshift.io/used-by: build-cloud + name: docker-build +spec: + description: | + This pipeline is ideal for building container images from a Containerfile while reducing network traffic. + + _Uses `buildah` to create a container image. It also optionally creates a source image and runs some build-time tests. EC will flag a violation for [`trusted_task.trusted`](https://enterprisecontract.dev/docs/ec-policies/release_policy.html#trusted_task__trusted) if any tasks are added to the pipeline. + This pipeline is pushed as a Tekton bundle to [quay.io](https://quay.io/repository/konflux-ci/tekton-catalog/pipeline-docker-build?tab=tags)_ + finally: + - name: show-sbom + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + taskRef: + name: show-sbom + version: "0.1" + - name: show-summary + params: + - name: pipelinerun-name + value: $(context.pipelineRun.name) + - name: git-url + value: $(tasks.clone-repository.results.url)?rev=$(tasks.clone-repository.results.commit) + - name: image-url + value: $(params.output-image) + - name: build-task-status + value: $(tasks.build-image-index.status) + taskRef: + name: summary + version: "0.2" + workspaces: + - name: workspace + workspace: workspace + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: . + description: Path to the source code of an application's component from where + to build image. + name: path-context + type: string + - default: Dockerfile + description: Path to the Dockerfile inside the context specified by parameter + path-context + name: dockerfile + type: string + - default: "false" + description: Force rebuild image + name: rebuild + type: string + - default: "false" + description: Skip checks against built image + name: skip-checks + type: string + - default: "false" + description: Execute the build with network isolation + name: hermetic + type: string + - default: "" + description: Build dependencies to be prefetched by Cachi2 + name: prefetch-input + type: string + - default: "" + description: Image tag expiration time, time values could be something like 1h, + 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + - default: "false" + description: Build a source image. + name: build-source-image + type: string + - default: "false" + description: Add built image into an OCI image index + name: build-image-index + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) for buildah + name: build-args + type: array + - default: "" + description: Path to a file with build arguments for buildah, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: build-args-file + type: string + results: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + tasks: + - name: init + params: + - name: image-url + value: $(params.output-image) + - name: rebuild + value: $(params.rebuild) + - name: skip-checks + value: $(params.skip-checks) + taskRef: + name: init + version: "0.2" + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + runAfter: + - init + taskRef: + name: git-clone + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: output + workspace: workspace + - name: basic-auth + workspace: git-auth + - name: prefetch-dependencies + params: + - name: input + value: $(params.prefetch-input) + runAfter: + - clone-repository + taskRef: + name: prefetch-dependencies + version: "0.1" + when: + - input: $(params.prefetch-input) + operator: notin + values: + - "" + workspaces: + - name: source + workspace: workspace + - name: git-basic-auth + workspace: git-auth + - name: netrc + workspace: netrc + - name: build-container + params: + - name: IMAGE + value: $(params.output-image) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: HERMETIC + value: $(params.hermetic) + - name: PREFETCH_INPUT + value: $(params.prefetch-input) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: BUILD_ARGS + value: + - $(params.build-args[*]) + - name: BUILD_ARGS_FILE + value: $(params.build-args-file) + runAfter: + - prefetch-dependencies + taskRef: + name: buildah + version: "0.3" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: source + workspace: workspace + - name: build-image-index + params: + - name: IMAGE + value: $(params.output-image) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: ALWAYS_BUILD_INDEX + value: $(params.build-image-index) + - name: IMAGES + value: + - $(tasks.build-container.results.IMAGE_URL)@$(tasks.build-container.results.IMAGE_DIGEST) + runAfter: + - build-container + taskRef: + name: build-image-index + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + - name: build-source-image + params: + - name: BINARY_IMAGE + value: $(params.output-image) + runAfter: + - build-image-index + taskRef: + name: source-build + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + - input: $(params.build-source-image) + operator: in + values: + - "true" + workspaces: + - name: workspace + workspace: workspace + - name: deprecated-base-image-check + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: deprecated-image-check + version: "0.4" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: clair-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: clair-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: ecosystem-cert-preflight-checks + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: ecosystem-cert-preflight-checks + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-snyk-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: sast-snyk-check + version: "0.3" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: clamav-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: clamav-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-coverity-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - coverity-availability-check + taskRef: + name: sast-coverity-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - input: $(tasks.coverity-availability-check.results.STATUS) + operator: in + values: + - success + workspaces: + - name: workspace + workspace: workspace + - name: coverity-availability-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: coverity-availability-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: sast-shell-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: sast-shell-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: sast-unicode-check + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: sast-unicode-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: apply-tags + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: apply-tags + version: "0.1" + - name: push-dockerfile + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + runAfter: + - build-image-index + taskRef: + name: push-dockerfile + version: "0.1" + workspaces: + - name: workspace + workspace: workspace + - name: rpms-signature-scan + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: rpms-signature-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + - name: git-auth + optional: true + - name: netrc + optional: true diff --git a/pipelines/fbc-builder/fbc-builder.yaml b/pipelines/fbc-builder/fbc-builder.yaml new file mode 100644 index 0000000000..4af194f3ab --- /dev/null +++ b/pipelines/fbc-builder/fbc-builder.yaml @@ -0,0 +1,261 @@ +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + labels: + pipelines.openshift.io/runtime: fbc + pipelines.openshift.io/strategy: fbc + pipelines.openshift.io/used-by: build-cloud + name: fbc-builder +spec: + description: | + This pipeline is ideal for building and verifying [file-based catalogs](https://konflux-ci.dev/docs/advanced-how-tos/building-olm.adoc#building-the-file-based-catalog). + + _Uses `buildah` to create a container image. Its build-time tests are limited to verifying the included catalog and do not scan the image. + This pipeline is pushed as a Tekton bundle to [quay.io](https://quay.io/repository/konflux-ci/tekton-catalog/pipeline-fbc-builder?tab=tags)_ + finally: + - name: show-sbom + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + taskRef: + name: show-sbom + version: "0.1" + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: . + description: Path to the source code of an application's component from where + to build image. + name: path-context + type: string + - default: Dockerfile + description: Path to the Dockerfile inside the context specified by parameter + path-context + name: dockerfile + type: string + - default: "false" + description: Force rebuild image + name: rebuild + type: string + - default: "false" + description: Skip checks against built image + name: skip-checks + type: string + - default: "true" + description: Execute the build with network isolation + name: hermetic + type: string + - default: "" + description: Build dependencies to be prefetched by Cachi2 + name: prefetch-input + type: string + - default: "" + description: Image tag expiration time, time values could be something like 1h, + 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + - default: "false" + description: Build a source image. + name: build-source-image + type: string + - default: "true" + description: Add built image into an OCI image index + name: build-image-index + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) for buildah + name: build-args + type: array + - default: "" + description: Path to a file with build arguments for buildah, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: build-args-file + type: string + - default: + - linux/x86_64 + description: List of platforms to build the container images on. The available + set of values is determined by the configuration of the multi-platform-controller. + name: build-platforms + type: array + results: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + tasks: + - name: init + params: + - name: image-url + value: $(params.output-image) + - name: rebuild + value: $(params.rebuild) + - name: skip-checks + value: $(params.skip-checks) + taskRef: + name: init + version: "0.2" + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + - name: ociStorage + value: $(params.output-image).git + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + runAfter: + - init + taskRef: + name: git-clone-oci-ta + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: basic-auth + workspace: git-auth + - name: prefetch-dependencies + params: + - name: input + value: $(params.prefetch-input) + - name: SOURCE_ARTIFACT + value: $(tasks.clone-repository.results.SOURCE_ARTIFACT) + - name: ociStorage + value: $(params.output-image).prefetch + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + runAfter: + - clone-repository + taskRef: + name: prefetch-dependencies-oci-ta + version: "0.1" + workspaces: + - name: git-basic-auth + workspace: git-auth + - name: netrc + workspace: netrc + - matrix: + params: + - name: PLATFORM + value: + - $(params.build-platforms) + name: build-images + params: + - name: IMAGE + value: $(params.output-image) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: HERMETIC + value: $(params.hermetic) + - name: PREFETCH_INPUT + value: $(params.prefetch-input) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: BUILD_ARGS + value: + - $(params.build-args[*]) + - name: BUILD_ARGS_FILE + value: $(params.build-args-file) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + - name: IMAGE_APPEND_PLATFORM + value: "true" + runAfter: + - clone-repository + taskRef: + name: buildah-remote-oci-ta + version: "0.3" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: [] + - name: build-image-index + params: + - name: IMAGE + value: $(params.output-image) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: ALWAYS_BUILD_INDEX + value: $(params.build-image-index) + - name: IMAGES + value: + - $(tasks.build-images.results.IMAGE_REF[*]) + runAfter: + - build-images + taskRef: + name: build-image-index + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + - name: deprecated-base-image-check + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: deprecated-image-check + version: "0.4" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: apply-tags + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: apply-tags + version: "0.1" + - name: validate-fbc + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: validate-fbc + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: git-auth + optional: true + - name: netrc + optional: true diff --git a/pipelines/maven-zip-build-oci-ta/maven-zip-build-oci-ta.yaml b/pipelines/maven-zip-build-oci-ta/maven-zip-build-oci-ta.yaml new file mode 100644 index 0000000000..484bd8724d --- /dev/null +++ b/pipelines/maven-zip-build-oci-ta/maven-zip-build-oci-ta.yaml @@ -0,0 +1,245 @@ +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + labels: + pipelines.openshift.io/runtime: generic + pipelines.openshift.io/strategy: maven-zip + pipelines.openshift.io/used-by: build-cloud + name: maven-zip-build-oci-ta +spec: + description: | + This pipeline will build the maven zip to oci-artifact while maintaining trust after pipeline customization. + + _Uses `prefetch-dependencies` to fetch all artifacts which will be the content of the maven zip, and then uses `build-maven-zip-oci-ta` to create zip and push it to quay.io as oci-artifact. Information is shared between tasks using OCI artifacts instead of PVCs. + This pipeline is pushed as a Tekton bundle to [quay.io](https://quay.io/repository/konflux-ci/tekton-catalog/pipeline-maven-zip-build-oci-ta?tab=tags)_ + finally: + - name: show-sbom + params: + - name: IMAGE_URL + value: $(tasks.build-oci-artifact.results.IMAGE_URL) + taskRef: + name: show-sbom + version: "0.1" + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: "false" + description: Force rebuild image + name: rebuild + type: string + - default: "false" + description: Skip checks against built image + name: skip-checks + type: string + - default: generic + description: Build dependencies to be prefetched by Cachi2 + name: prefetch-input + type: string + - default: "" + description: Image tag expiration time, time values could be something like 1h, + 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + results: + - name: IMAGE_URL + value: $(tasks.build-oci-artifact.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-oci-artifact.results.IMAGE_DIGEST) + - name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + tasks: + - name: init + params: + - name: image-url + value: $(params.output-image) + - name: rebuild + value: $(params.rebuild) + - name: skip-checks + value: $(params.skip-checks) + taskRef: + name: init + version: "0.2" + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + - name: ociStorage + value: $(params.output-image).git + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + runAfter: + - init + taskRef: + name: git-clone-oci-ta + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: basic-auth + workspace: git-auth + - name: prefetch-dependencies + params: + - name: input + value: $(params.prefetch-input) + - name: SOURCE_ARTIFACT + value: $(tasks.clone-repository.results.SOURCE_ARTIFACT) + - name: ociStorage + value: $(params.output-image).prefetch + - name: ociArtifactExpiresAfter + value: $(params.image-expires-after) + runAfter: + - clone-repository + taskRef: + name: prefetch-dependencies-oci-ta + version: "0.1" + workspaces: + - name: git-basic-auth + workspace: git-auth + - name: netrc + workspace: netrc + - name: build-oci-artifact + params: + - name: IMAGE + value: $(params.output-image) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - prefetch-dependencies + taskRef: + name: build-maven-zip-oci-ta + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: [] + - name: sast-snyk-check + params: + - name: image-digest + value: $(tasks.build-oci-artifact.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-oci-artifact.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-oci-artifact + taskRef: + name: sast-snyk-check-oci-ta + version: "0.3" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: [] + - name: sast-coverity-check + params: + - name: image-digest + value: $(tasks.build-oci-artifact.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-oci-artifact.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - coverity-availability-check + taskRef: + name: sast-coverity-check-oci-ta + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - input: $(tasks.coverity-availability-check.results.STATUS) + operator: in + values: + - success + workspaces: [] + - name: coverity-availability-check + params: + - name: image-digest + value: $(tasks.build-oci-artifact.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-oci-artifact.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-oci-artifact + taskRef: + name: coverity-availability-check-oci-ta + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: [] + - name: sast-shell-check + params: + - name: image-digest + value: $(tasks.build-oci-artifact.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-oci-artifact.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-oci-artifact + taskRef: + name: sast-shell-check-oci-ta + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: [] + - name: sast-unicode-check + params: + - name: image-url + value: $(tasks.build-oci-artifact.results.IMAGE_URL) + - name: SOURCE_ARTIFACT + value: $(tasks.prefetch-dependencies.results.SOURCE_ARTIFACT) + - name: CACHI2_ARTIFACT + value: $(tasks.prefetch-dependencies.results.CACHI2_ARTIFACT) + runAfter: + - build-oci-artifact + taskRef: + name: sast-shell-check-oci-ta + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: [] + workspaces: + - name: workspace + - name: git-auth + optional: true + - name: netrc + optional: true diff --git a/pipelines/maven-zip-build/maven-zip-build.yaml b/pipelines/maven-zip-build/maven-zip-build.yaml new file mode 100644 index 0000000000..4e2ecbbaaa --- /dev/null +++ b/pipelines/maven-zip-build/maven-zip-build.yaml @@ -0,0 +1,250 @@ +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + labels: + pipelines.openshift.io/runtime: generic + pipelines.openshift.io/strategy: maven-zip + pipelines.openshift.io/used-by: build-cloud + name: maven-zip-build +spec: + description: | + This pipeline will build the maven zip to oci-artifact. + + _Uses `prefetch-dependencies` to fetch all artifacts which will be the content of the maven zip, and then uses `build-maven-zip` to create zip and push it to quay.io as oci-artifact. + This pipeline is pushed as a Tekton bundle to [quay.io](https://quay.io/repository/konflux-ci/tekton-catalog/pipeline-maven-zip-build?tab=tags)_ + finally: + - name: show-sbom + params: + - name: IMAGE_URL + value: $(tasks.build-oci-artifact.results.IMAGE_URL) + taskRef: + name: show-sbom + version: "0.1" + - name: show-summary + params: + - name: pipelinerun-name + value: $(context.pipelineRun.name) + - name: git-url + value: $(tasks.clone-repository.results.url)?rev=$(tasks.clone-repository.results.commit) + - name: image-url + value: $(params.output-image) + - name: build-task-status + value: $(tasks.build-oci-artifact.status) + taskRef: + name: summary + version: "0.2" + workspaces: + - name: workspace + workspace: workspace + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: "false" + description: Force rebuild image + name: rebuild + type: string + - default: "false" + description: Skip checks against built image + name: skip-checks + type: string + - default: generic + description: Build dependencies to be prefetched by Cachi2 + name: prefetch-input + type: string + - default: "" + description: Image tag expiration time, time values could be something like 1h, + 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + results: + - name: IMAGE_URL + value: $(tasks.build-oci-artifact.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-oci-artifact.results.IMAGE_DIGEST) + - name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + tasks: + - name: init + params: + - name: image-url + value: $(params.output-image) + - name: rebuild + value: $(params.rebuild) + - name: skip-checks + value: $(params.skip-checks) + taskRef: + name: init + version: "0.2" + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + runAfter: + - init + taskRef: + name: git-clone + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: output + workspace: workspace + - name: basic-auth + workspace: git-auth + - name: prefetch-dependencies + params: + - name: input + value: $(params.prefetch-input) + runAfter: + - clone-repository + taskRef: + name: prefetch-dependencies + version: "0.1" + when: + - input: $(params.prefetch-input) + operator: notin + values: + - "" + workspaces: + - name: source + workspace: workspace + - name: git-basic-auth + workspace: git-auth + - name: netrc + workspace: netrc + - name: build-oci-artifact + params: + - name: IMAGE + value: $(params.output-image) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + runAfter: + - prefetch-dependencies + taskRef: + name: build-maven-zip + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: source + workspace: workspace + - name: sast-snyk-check + params: + - name: image-digest + value: $(tasks.build-oci-artifact.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-oci-artifact.results.IMAGE_URL) + runAfter: + - build-oci-artifact + taskRef: + name: sast-snyk-check + version: "0.3" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: sast-coverity-check + params: + - name: image-digest + value: $(tasks.build-oci-artifact.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-oci-artifact.results.IMAGE_URL) + runAfter: + - coverity-availability-check + taskRef: + name: sast-coverity-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - input: $(tasks.coverity-availability-check.results.STATUS) + operator: in + values: + - success + workspaces: + - name: workspace + workspace: workspace + - name: coverity-availability-check + params: + - name: image-digest + value: $(tasks.build-oci-artifact.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-oci-artifact.results.IMAGE_URL) + runAfter: + - build-oci-artifact + taskRef: + name: coverity-availability-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: sast-shell-check + params: + - name: image-digest + value: $(tasks.build-oci-artifact.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-oci-artifact.results.IMAGE_URL) + runAfter: + - build-oci-artifact + taskRef: + name: sast-shell-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: sast-unicode-check + params: + - name: image-url + value: $(tasks.build-oci-artifact.results.IMAGE_URL) + runAfter: + - build-oci-artifact + taskRef: + name: sast-unicode-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + workspaces: + - name: workspace + - name: git-auth + optional: true + - name: netrc + optional: true diff --git a/pipelines/rhtap/rhtap.yaml b/pipelines/rhtap/rhtap.yaml new file mode 100644 index 0000000000..2c144c8dc0 --- /dev/null +++ b/pipelines/rhtap/rhtap.yaml @@ -0,0 +1,448 @@ +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + labels: + pipelines.openshift.io/runtime: generic + pipelines.openshift.io/strategy: docker + pipelines.openshift.io/used-by: build-cloud + name: docker-build-rhtap +spec: + finally: + - name: show-sbom + params: + - name: IMAGE_URL + value: $(tasks.build-container.results.IMAGE_URL) + taskRef: + name: show-sbom-rhdh + version: "0.1" + - name: show-summary + params: + - name: pipelinerun-name + value: $(context.pipelineRun.name) + - name: git-url + value: $(tasks.clone-repository.results.url)?rev=$(tasks.clone-repository.results.commit) + - name: image-url + value: $(params.output-image) + - name: build-task-status + value: $(tasks.build-container.status) + taskRef: + name: summary + version: "0.2" + workspaces: + - name: workspace + workspace: workspace + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: . + description: Path to the source code of an application's component from where + to build image. + name: path-context + type: string + - default: Dockerfile + description: Path to the Dockerfile inside the context specified by parameter + path-context + name: dockerfile + type: string + - default: "false" + description: Force rebuild image + name: rebuild + type: string + - default: "" + description: Image tag expiration time, time values could be something like 1h, + 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + - default: rox-api-token + name: stackrox-secret + type: string + - default: gitops-auth-secret + description: 'Secret name to enable this pipeline to update the gitops repo with + the new image. ' + name: gitops-auth-secret-name + type: string + - default: push + description: Event that triggered the pipeline run, e.g. push, pull_request + name: event-type + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) for buildah + name: build-args + type: array + - default: "" + description: Path to a file with build arguments for buildah, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: build-args-file + type: string + results: + - name: IMAGE_URL + value: $(tasks.build-container.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-container.results.IMAGE_DIGEST) + - name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + - name: ACS_SCAN_OUTPUT + value: $(tasks.acs-image-scan.results.SCAN_OUTPUT) + tasks: + - name: init + params: + - name: image-url + value: $(params.output-image) + - name: rebuild + value: $(params.rebuild) + taskRef: + name: init + version: "0.2" + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + runAfter: + - init + taskRef: + name: git-clone + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: output + workspace: workspace + - name: basic-auth + workspace: git-auth + - name: build-container + params: + - name: IMAGE + value: $(params.output-image) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: BUILD_ARGS + value: + - $(params.build-args[*]) + - name: BUILD_ARGS_FILE + value: $(params.build-args-file) + runAfter: + - clone-repository + taskRef: + name: buildah-rhtap + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: source + workspace: workspace + - name: sast-unicode-check + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: sast-unicode-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: apply-tags + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: apply-tags + version: "0.1" + - name: push-dockerfile + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + runAfter: + - build-image-index + taskRef: + name: push-dockerfile + version: "0.1" + workspaces: + - name: workspace + workspace: workspace + - name: rpms-signature-scan + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: rpms-signature-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: acs-image-check + params: + - name: rox-secret-name + value: $(params.stackrox-secret) + - name: image + value: $(params.output-image) + - name: insecure-skip-tls-verify + value: "true" + - name: image-digest + value: $(tasks.build-container.results.IMAGE_DIGEST) + runAfter: + - build-container + taskRef: + name: acs-image-check + - name: acs-image-scan + params: + - name: rox-secret-name + value: $(params.stackrox-secret) + - name: image + value: $(params.output-image) + - name: insecure-skip-tls-verify + value: "true" + - name: image-digest + value: $(tasks.build-container.results.IMAGE_DIGEST) + runAfter: + - build-container + taskRef: + kind: Task + name: acs-image-scan + - name: acs-deploy-check + params: + - name: rox-secret-name + value: $(params.stackrox-secret) + - name: gitops-repo-url + value: $(params.git-url)-gitops + - name: insecure-skip-tls-verify + value: "true" + runAfter: + - update-deployment + taskRef: + kind: Task + name: acs-deploy-check + when: + - input: $(params.event-type) + operator: in + values: + - push + - Push + - name: update-deployment + params: + - name: gitops-repo-url + value: $(params.git-url)-gitops + - name: image + value: $(tasks.build-container.results.IMAGE_URL)@$(tasks.build-container.results.IMAGE_DIGEST) + - name: gitops-auth-secret-name + value: $(params.gitops-auth-secret-name) + runAfter: + - build-container + taskRef: + kind: Task + name: update-deployment + when: + - input: $(params.event-type) + operator: notin + values: + - pull_request + - Merge Request + workspaces: + - name: workspace + - name: git-auth + optional: true +--- +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + name: gitops-pull-request +spec: + params: + - description: Gitops repo url + name: git-url + type: string + - default: "" + description: Gitops repo revision + name: revision + type: string + - default: main + description: The target branch for the pull request + name: target-branch + type: string + - default: github.com/enterprise-contract/config//default + description: Enterprise Contract policy to validate against + name: ec-policy-configuration + type: string + - default: "true" + description: Should EC violations cause the pipeline to fail? + name: ec-strict + type: string + - default: k8s://$(context.pipelineRun.namespace)/cosign-pub + description: The public key that EC should use to verify signatures + name: ec-public-key + type: string + - default: http://rekor-server.rhtap-tas.svc + description: The Rekor host that EC should use to look up transparency logs + name: ec-rekor-host + type: string + - default: http://tuf.rhtap-tas.svc + description: The TUF mirror that EC should use + name: ec-tuf-mirror + type: string + - default: tpa-secret + description: The name of the Secret that contains Trustification (TPA) configuration + name: trustification-secret-name + type: string + - default: "true" + description: Should the pipeline fail when there are SBOMs to upload but Trustification + is not properly configured (i.e. the secret is missing or doesn't have all the + required keys)? + name: fail-if-trustification-not-configured + type: string + tasks: + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + - name: fetchTags + value: "true" + taskRef: + name: git-clone + version: "0.1" + workspaces: + - name: output + workspace: workspace + - name: basic-auth + workspace: git-auth + - name: get-images-to-verify + params: + - name: TARGET_BRANCH + value: $(params.target-branch) + runAfter: + - clone-repository + taskRef: + name: gather-deploy-images + version: "0.1" + workspaces: + - name: source + workspace: workspace + - name: verify-enterprise-contract + params: + - name: IMAGES + value: $(tasks.get-images-to-verify.results.IMAGES_TO_VERIFY) + - name: STRICT + value: $(params.ec-strict) + - name: POLICY_CONFIGURATION + value: $(params.ec-policy-configuration) + - name: PUBLIC_KEY + value: $(params.ec-public-key) + - name: REKOR_HOST + value: $(params.ec-rekor-host) + - name: TUF_MIRROR + value: $(params.ec-tuf-mirror) + runAfter: + - get-images-to-verify + taskRef: + name: verify-enterprise-contract + version: "0.1" + when: + - input: $(tasks.get-images-to-verify.results.IMAGES_TO_VERIFY) + operator: notin + values: + - "" + - name: get-images-to-upload-sbom + params: + - name: TARGET_BRANCH + value: $(params.target-branch) + - name: FROM_ENVIRONMENTS + value: + - stage + - prod + runAfter: + - clone-repository + taskRef: + name: gather-deploy-images + version: "0.1" + workspaces: + - name: source + workspace: workspace + - name: download-sboms + params: + - name: IMAGES + value: $(tasks.get-images-to-upload-sbom.results.IMAGES_TO_VERIFY) + - name: SBOMS_DIR + value: sboms + - name: PUBLIC_KEY + value: $(params.ec-public-key) + - name: REKOR_HOST + value: $(params.ec-rekor-host) + - name: TUF_MIRROR + value: $(params.ec-tuf-mirror) + runAfter: + - get-images-to-upload-sbom + taskRef: + name: download-sbom-from-url-in-attestation + version: "0.1" + when: + - input: $(tasks.get-images-to-upload-sbom.results.IMAGES_TO_VERIFY) + operator: notin + values: + - "" + workspaces: + - name: sboms + workspace: workspace + - name: upload-sboms-to-trustification + params: + - name: SBOMS_DIR + value: sboms + - name: TRUSTIFICATION_SECRET_NAME + value: $(params.trustification-secret-name) + - name: FAIL_IF_TRUSTIFICATION_NOT_CONFIGURED + value: $(params.fail-if-trustification-not-configured) + runAfter: + - download-sboms + taskRef: + name: upload-sbom-to-trustification + version: "0.1" + when: + - input: $(tasks.get-images-to-upload-sbom.results.IMAGES_TO_VERIFY) + operator: notin + values: + - "" + workspaces: + - name: sboms + workspace: workspace diff --git a/pipelines/tekton-bundle-builder/tekton-bundle-builder.yaml b/pipelines/tekton-bundle-builder/tekton-bundle-builder.yaml new file mode 100644 index 0000000000..d6e3ffa1ba --- /dev/null +++ b/pipelines/tekton-bundle-builder/tekton-bundle-builder.yaml @@ -0,0 +1,269 @@ +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + labels: + pipelines.openshift.io/runtime: "" + pipelines.openshift.io/strategy: "" + pipelines.openshift.io/used-by: "" + name: tekton-bundle-builder +spec: + finally: + - name: show-summary + params: + - name: pipelinerun-name + value: $(context.pipelineRun.name) + - name: git-url + value: $(tasks.clone-repository.results.url)?rev=$(tasks.clone-repository.results.commit) + - name: image-url + value: $(params.output-image) + - name: build-task-status + value: $(tasks.build-image-index.status) + taskRef: + name: summary + version: "0.2" + workspaces: + - name: workspace + workspace: workspace + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: . + description: Path to the source code of an application's component from where + to build image. + name: path-context + type: string + - default: Dockerfile + description: Path to the Dockerfile inside the context specified by parameter + path-context + name: dockerfile + type: string + - default: "false" + description: Force rebuild image + name: rebuild + type: string + - default: "false" + description: Skip checks against built image + name: skip-checks + type: string + - default: "false" + description: Execute the build with network isolation + name: hermetic + type: string + - default: "" + description: Build dependencies to be prefetched by Cachi2 + name: prefetch-input + type: string + - default: "" + description: Image tag expiration time, time values could be something like 1h, + 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + - default: "false" + description: Build a source image. + name: build-source-image + type: string + - default: "false" + description: Add built image into an OCI image index + name: build-image-index + type: string + results: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + tasks: + - name: init + params: + - name: image-url + value: $(params.output-image) + - name: rebuild + value: $(params.rebuild) + - name: skip-checks + value: $(params.skip-checks) + taskRef: + name: init + version: "0.2" + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + runAfter: + - init + taskRef: + name: git-clone + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: output + workspace: workspace + - name: basic-auth + workspace: git-auth + - name: prefetch-dependencies + params: + - name: input + value: $(params.prefetch-input) + runAfter: + - clone-repository + taskRef: + name: prefetch-dependencies + version: "0.1" + when: + - input: $(params.prefetch-input) + operator: notin + values: + - "" + workspaces: + - name: source + workspace: workspace + - name: git-basic-auth + workspace: git-auth + - name: netrc + workspace: netrc + - name: build-container + params: + - name: IMAGE + value: $(params.output-image) + - name: CONTEXT + value: $(params.path-context) + runAfter: + - prefetch-dependencies + taskRef: + name: tkn-bundle + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: source + workspace: workspace + - name: build-image-index + params: + - name: IMAGE + value: $(params.output-image) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: ALWAYS_BUILD_INDEX + value: $(params.build-image-index) + - name: IMAGES + value: + - $(tasks.build-container.results.IMAGE_URL)@$(tasks.build-container.results.IMAGE_DIGEST) + runAfter: + - build-container + taskRef: + name: build-image-index + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + - name: sast-coverity-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - coverity-availability-check + taskRef: + name: sast-coverity-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - input: $(tasks.coverity-availability-check.results.STATUS) + operator: in + values: + - success + workspaces: + - name: workspace + workspace: workspace + - name: sast-unicode-check + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: sast-unicode-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: apply-tags + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: apply-tags + version: "0.1" + - name: push-dockerfile + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + runAfter: + - build-image-index + taskRef: + name: push-dockerfile + version: "0.1" + workspaces: + - name: workspace + workspace: workspace + - name: rpms-signature-scan + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: rpms-signature-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + - name: git-auth + optional: true + - name: netrc + optional: true diff --git a/pipelines/template-build/template-build.yaml b/pipelines/template-build/template-build.yaml index 096f330785..5d7f4c8c7e 100644 --- a/pipelines/template-build/template-build.yaml +++ b/pipelines/template-build/template-build.yaml @@ -1,394 +1,410 @@ -# This build pipeline template is intended to be processed by scripts -# under hack/ directory rather than by kustomize directly. - apiVersion: tekton.dev/v1 kind: Pipeline metadata: - name: template-build labels: - "pipelines.openshift.io/used-by": "" - "pipelines.openshift.io/runtime": "" - "pipelines.openshift.io/strategy": "" + pipelines.openshift.io/runtime: "" + pipelines.openshift.io/strategy: "" + pipelines.openshift.io/used-by: "" + name: template-build spec: - params: - - description: 'Source Repository URL' - name: git-url - type: string - - description: 'Revision of the Source Repository' - name: revision - type: string - default: "" - - description: 'Fully Qualified Output Image' - name: output-image - type: string - - description: Path to the source code of an application's component from where to build image. - name: path-context - type: string - default: . - - description: Path to the Dockerfile inside the context specified by parameter path-context - name: dockerfile - type: string - default: Dockerfile - - description: Force rebuild image - name: rebuild - type: string - default: "false" - - description: Skip checks against built image - name: skip-checks - type: string - default: "false" - - description: Execute the build with network isolation - name: hermetic - type: string - default: "false" - - description: Build dependencies to be prefetched by Cachi2 - name: prefetch-input - type: string - default: "" - - description: Image tag expiration time, time values could be something like 1h, 2d, 3w for hours, days, and weeks, respectively. - name: image-expires-after - default: "" - - name: build-source-image - description: Build a source image. - type: string - default: "false" - - name: build-image-index - description: Add built image into an OCI image index - type: string - default: "false" - tasks: - - name: init - params: - - name: image-url - value: "$(params.output-image)" - - name: rebuild - value: "$(params.rebuild)" - - name: skip-checks - value: "$(params.skip-checks)" - taskRef: - name: init - # A pointer for referencing the correct version of task in the built pipeline bundles. - version: "0.2" - - name: clone-repository - when: - - input: $(tasks.init.results.build) - operator: in - values: ["true"] - runAfter: - - init - params: - - name: url - value: $(params.git-url) - - name: revision - value: "$(params.revision)" - taskRef: - name: git-clone - version: "0.1" - workspaces: - - name: output - workspace: workspace - - name: basic-auth - workspace: git-auth - - name: prefetch-dependencies - when: - - input: $(params.prefetch-input) - operator: notin - values: [""] - params: - - name: input - value: $(params.prefetch-input) - runAfter: - - clone-repository - taskRef: - name: prefetch-dependencies - version: "0.1" - workspaces: - - name: source - workspace: workspace - - name: git-basic-auth - workspace: git-auth - - name: netrc - workspace: netrc - - name: build-container - when: - - input: $(tasks.init.results.build) - operator: in - values: ["true"] - runAfter: - - prefetch-dependencies - taskRef: - name: $REPLACE_ME - workspaces: - - name: source - workspace: workspace - - name: build-image-index - when: - - input: $(tasks.init.results.build) - operator: in - values: ["true"] - runAfter: - - build-container - taskRef: - name: build-image-index - version: "0.1" - params: - - name: IMAGE - value: $(params.output-image) - - name: COMMIT_SHA - value: $(tasks.clone-repository.results.commit) - - name: IMAGE_EXPIRES_AFTER - value: "$(params.image-expires-after)" - - name: ALWAYS_BUILD_INDEX - value: $(params.build-image-index) - - name: IMAGES - value: - - $(tasks.build-container.results.IMAGE_URL)@$(tasks.build-container.results.IMAGE_DIGEST) - - name: build-source-image - when: - - input: $(tasks.init.results.build) - operator: in - values: ["true"] - - input: $(params.build-source-image) - operator: in - values: ["true"] - runAfter: - - build-image-index - taskRef: - name: source-build - version: "0.1" - params: - - name: BINARY_IMAGE - value: "$(params.output-image)" - workspaces: - - name: workspace - workspace: workspace - - name: deprecated-base-image-check - when: - - input: $(params.skip-checks) - operator: in - values: ["false"] - taskRef: - name: deprecated-image-check - version: "0.4" - params: - - name: IMAGE_URL - value: $(tasks.build-image-index.results.IMAGE_URL) - - name: IMAGE_DIGEST - value: $(tasks.build-image-index.results.IMAGE_DIGEST) - runAfter: - - build-image-index - - name: clair-scan - when: - - input: $(params.skip-checks) - operator: in - values: ["false"] - runAfter: - - build-image-index - taskRef: - name: clair-scan - version: "0.2" - params: - - name: image-digest - value: $(tasks.build-image-index.results.IMAGE_DIGEST) - - name: image-url - value: $(tasks.build-image-index.results.IMAGE_URL) - - name: ecosystem-cert-preflight-checks - when: - - input: $(params.skip-checks) - operator: in - values: ["false"] - runAfter: - - build-image-index - taskRef: - name: ecosystem-cert-preflight-checks - version: "0.1" - params: - - name: image-url - value: $(tasks.build-image-index.results.IMAGE_URL) - - name: sast-snyk-check - when: - - input: $(params.skip-checks) - operator: in - values: ["false"] - runAfter: - - build-image-index - taskRef: - name: sast-snyk-check - version: "0.3" - workspaces: - - name: workspace - workspace: workspace - params: - - name: image-digest - value: $(tasks.build-image-index.results.IMAGE_DIGEST) - - name: image-url - value: $(tasks.build-image-index.results.IMAGE_URL) - - name: clamav-scan - when: - - input: $(params.skip-checks) - operator: in - values: ["false"] - runAfter: - - build-image-index - taskRef: - name: clamav-scan - version: "0.2" - params: - - name: image-digest - value: $(tasks.build-image-index.results.IMAGE_DIGEST) - - name: image-url - value: $(tasks.build-image-index.results.IMAGE_URL) - - name: sast-coverity-check - when: - - input: $(params.skip-checks) - operator: in - values: ["false"] - - input: $(tasks.coverity-availability-check.results.STATUS) - operator: in - values: ["success"] - runAfter: - - coverity-availability-check - taskRef: - name: sast-coverity-check - version: "0.1" - params: - - name: image-digest - value: $(tasks.build-image-index.results.IMAGE_DIGEST) - - name: image-url - value: $(tasks.build-image-index.results.IMAGE_URL) - workspaces: - - name: workspace - workspace: workspace - - name: coverity-availability-check - when: - - input: $(params.skip-checks) - operator: in - values: ["false"] - runAfter: - - build-image-index - taskRef: - name: coverity-availability-check - version: "0.1" - params: - - name: image-digest - value: $(tasks.build-image-index.results.IMAGE_DIGEST) - - name: image-url - value: $(tasks.build-image-index.results.IMAGE_URL) - workspaces: - - name: workspace - workspace: workspace - - name: sast-shell-check - when: - - input: $(params.skip-checks) - operator: in - values: ["false"] - runAfter: - - build-image-index - taskRef: - name: sast-shell-check - version: "0.1" - workspaces: - - name: workspace - workspace: workspace - params: - - name: image-digest - value: $(tasks.build-image-index.results.IMAGE_DIGEST) - - name: image-url - value: $(tasks.build-image-index.results.IMAGE_URL) - - name: sast-unicode-check - when: - - input: $(params.skip-checks) - operator: in - values: ["false"] - runAfter: - - build-image-index - taskRef: - name: sast-unicode-check - version: "0.1" - workspaces: - - name: workspace - workspace: workspace - params: - - name: image-url - value: $(tasks.build-image-index.results.IMAGE_URL) - - name: apply-tags - runAfter: - - build-image-index - taskRef: - name: apply-tags - version: "0.1" - params: - - name: IMAGE - value: $(tasks.build-image-index.results.IMAGE_URL) - - name: push-dockerfile - runAfter: - - build-image-index - taskRef: - name: push-dockerfile - version: "0.1" - params: - - name: IMAGE - value: $(tasks.build-image-index.results.IMAGE_URL) - - name: IMAGE_DIGEST - value: $(tasks.build-image-index.results.IMAGE_DIGEST) - - name: DOCKERFILE - value: $(params.dockerfile) - - name: CONTEXT - value: $(params.path-context) - workspaces: - - name: workspace - workspace: workspace - - name: rpms-signature-scan - when: - - input: $(params.skip-checks) - operator: in - values: ["false"] - runAfter: - - build-image-index - taskRef: - name: rpms-signature-scan - version: "0.2" - params: - - name: image-url - value: $(tasks.build-image-index.results.IMAGE_URL) - - name: image-digest - value: $(tasks.build-image-index.results.IMAGE_DIGEST) finally: - - name: show-sbom - taskRef: - name: show-sbom - version: "0.1" - params: - - name: IMAGE_URL - value: $(tasks.build-image-index.results.IMAGE_URL) - - name: show-summary - taskRef: - name: summary - version: "0.2" - params: - - name: pipelinerun-name - value: "$(context.pipelineRun.name)" - - name: git-url - value: "$(tasks.clone-repository.results.url)?rev=$(tasks.clone-repository.results.commit)" - - name: image-url - value: $(params.output-image) - - name: build-task-status - value: $(tasks.build-image-index.status) - workspaces: - - name: workspace - workspace: workspace + - name: show-sbom + params: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + taskRef: + name: show-sbom + version: "0.1" + - name: show-summary + params: + - name: pipelinerun-name + value: $(context.pipelineRun.name) + - name: git-url + value: $(tasks.clone-repository.results.url)?rev=$(tasks.clone-repository.results.commit) + - name: image-url + value: $(params.output-image) + - name: build-task-status + value: $(tasks.build-image-index.status) + taskRef: + name: summary + version: "0.2" + workspaces: + - name: workspace + workspace: workspace + params: + - description: Source Repository URL + name: git-url + type: string + - default: "" + description: Revision of the Source Repository + name: revision + type: string + - description: Fully Qualified Output Image + name: output-image + type: string + - default: . + description: Path to the source code of an application's component from where + to build image. + name: path-context + type: string + - default: Dockerfile + description: Path to the Dockerfile inside the context specified by parameter + path-context + name: dockerfile + type: string + - default: "false" + description: Force rebuild image + name: rebuild + type: string + - default: "false" + description: Skip checks against built image + name: skip-checks + type: string + - default: "false" + description: Execute the build with network isolation + name: hermetic + type: string + - default: "" + description: Build dependencies to be prefetched by Cachi2 + name: prefetch-input + type: string + - default: "" + description: Image tag expiration time, time values could be something like 1h, + 2d, 3w for hours, days, and weeks, respectively. + name: image-expires-after + - default: "false" + description: Build a source image. + name: build-source-image + type: string + - default: "false" + description: Add built image into an OCI image index + name: build-image-index + type: string results: + - name: IMAGE_URL + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: CHAINS-GIT_URL + value: $(tasks.clone-repository.results.url) + - name: CHAINS-GIT_COMMIT + value: $(tasks.clone-repository.results.commit) + tasks: + - name: init + params: + - name: image-url + value: $(params.output-image) + - name: rebuild + value: $(params.rebuild) + - name: skip-checks + value: $(params.skip-checks) + taskRef: + name: init + version: "0.2" + - name: clone-repository + params: + - name: url + value: $(params.git-url) + - name: revision + value: $(params.revision) + runAfter: + - init + taskRef: + name: git-clone + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: output + workspace: workspace + - name: basic-auth + workspace: git-auth + - name: prefetch-dependencies + params: + - name: input + value: $(params.prefetch-input) + runAfter: + - clone-repository + taskRef: + name: prefetch-dependencies + version: "0.1" + when: + - input: $(params.prefetch-input) + operator: notin + values: + - "" + workspaces: + - name: source + workspace: workspace + - name: git-basic-auth + workspace: git-auth + - name: netrc + workspace: netrc + - name: build-container + runAfter: + - prefetch-dependencies + taskRef: + name: $REPLACE_ME + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + workspaces: + - name: source + workspace: workspace + - name: build-image-index + params: + - name: IMAGE + value: $(params.output-image) + - name: COMMIT_SHA + value: $(tasks.clone-repository.results.commit) + - name: IMAGE_EXPIRES_AFTER + value: $(params.image-expires-after) + - name: ALWAYS_BUILD_INDEX + value: $(params.build-image-index) + - name: IMAGES + value: + - $(tasks.build-container.results.IMAGE_URL)@$(tasks.build-container.results.IMAGE_DIGEST) + runAfter: + - build-container + taskRef: + name: build-image-index + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + - name: build-source-image + params: + - name: BINARY_IMAGE + value: $(params.output-image) + runAfter: + - build-image-index + taskRef: + name: source-build + version: "0.1" + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" + - input: $(params.build-source-image) + operator: in + values: + - "true" + workspaces: + - name: workspace + workspace: workspace + - name: deprecated-base-image-check + params: - name: IMAGE_URL - value: "$(tasks.build-image-index.results.IMAGE_URL)" + value: $(tasks.build-image-index.results.IMAGE_URL) - name: IMAGE_DIGEST - value: "$(tasks.build-image-index.results.IMAGE_DIGEST)" - - name: CHAINS-GIT_URL - value: "$(tasks.clone-repository.results.url)" - - name: CHAINS-GIT_COMMIT - value: "$(tasks.clone-repository.results.commit)" - workspaces: + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: deprecated-image-check + version: "0.4" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: clair-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: clair-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: ecosystem-cert-preflight-checks + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: ecosystem-cert-preflight-checks + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-snyk-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: sast-snyk-check + version: "0.3" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: - name: workspace - - name: git-auth - optional: true - - name: netrc - optional: true + workspace: workspace + - name: clamav-scan + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: clamav-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - name: sast-coverity-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - coverity-availability-check + taskRef: + name: sast-coverity-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + - input: $(tasks.coverity-availability-check.results.STATUS) + operator: in + values: + - success + workspaces: + - name: workspace + workspace: workspace + - name: coverity-availability-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: coverity-availability-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: sast-shell-check + params: + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: sast-shell-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: sast-unicode-check + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: sast-unicode-check + version: "0.1" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: apply-tags + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: apply-tags + version: "0.1" + - name: push-dockerfile + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + runAfter: + - build-image-index + taskRef: + name: push-dockerfile + version: "0.1" + workspaces: + - name: workspace + workspace: workspace + - name: rpms-signature-scan + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: rpms-signature-scan + version: "0.2" + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + - name: git-auth + optional: true + - name: netrc + optional: true diff --git a/task/build-image-index/0.1/build-image-index.yaml b/task/build-image-index/0.1/build-image-index.yaml index 74a5197e71..19222add73 100644 --- a/task/build-image-index/0.1/build-image-index.yaml +++ b/task/build-image-index/0.1/build-image-index.yaml @@ -1,43 +1,48 @@ apiVersion: tekton.dev/v1 kind: Task metadata: + annotations: + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux labels: app.kubernetes.io/version: "0.1" - build.appstudio.redhat.com/build_type: "docker" - annotations: - tekton.dev/pipelines.minVersion: "0.12.1" - tekton.dev/tags: "image-build, konflux" + build.appstudio.redhat.com/build_type: docker name: build-image-index spec: - description: |- - This takes existing Image Manifests and combines them in an Image Index. + description: This takes existing Image Manifests and combines them in an Image Index. params: - - name: IMAGE - description: The target image and tag where the image will be pushed to. + - description: The target image and tag where the image will be pushed to. + name: IMAGE type: string - - name: TLSVERIFY - description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS registry) + - default: "true" + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) + name: TLSVERIFY type: string - default: "true" - - name: COMMIT_SHA + - default: "" description: The commit the image is built from. + name: COMMIT_SHA type: string - default: "" - - name: IMAGES - description: List of Image Manifests to be referenced by the Image Index + - description: List of Image Manifests to be referenced by the Image Index + name: IMAGES type: array - - name: IMAGE_EXPIRES_AFTER - description: Delete image tag after specified time resulting in garbage collection of the digest. Empty means to keep the image tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, respectively. + - default: "" + description: Delete image tag after specified time resulting in garbage collection + of the digest. Empty means to keep the image tag. Time values could be something + like 1h, 2d, 3w for hours, days, and weeks, respectively. + name: IMAGE_EXPIRES_AFTER type: string - default: "" - - name: ALWAYS_BUILD_INDEX - description: Build an image index even if IMAGES is of length 1. Default true. If the image index generation is skipped, the task will forward values for params.IMAGES[0] to results.IMAGE_*. In order to properly set all results, use the repository:tag@sha256:digest format for the IMAGES parameter. + - default: "true" + description: Build an image index even if IMAGES is of length 1. Default true. + If the image index generation is skipped, the task will forward values for params.IMAGES[0] + to results.IMAGE_*. In order to properly set all results, use the repository:tag@sha256:digest + format for the IMAGES parameter. + name: ALWAYS_BUILD_INDEX type: string - default: "true" - - name: STORAGE_DRIVER + - default: vfs description: Storage driver to configure for buildah + name: STORAGE_DRIVER type: string - default: vfs results: - description: Digest of the image just built name: IMAGE_DIGEST @@ -45,15 +50,13 @@ spec: name: IMAGE_URL - description: List of all referenced image manifests name: IMAGES - - description: Image reference of the built image containing both the repository and the digest + - description: Image reference of the built image containing both the repository + and the digest name: IMAGE_REF - - name: SBOM_BLOB_URL - description: Reference of SBOM blob digest to enable digest-based verification from provenance + - description: Reference of SBOM blob digest to enable digest-based verification + from provenance + name: SBOM_BLOB_URL type: string - volumes: - - name: shared-dir - emptyDir: {} - stepTemplate: env: - name: BUILDAH_FORMAT @@ -69,20 +72,19 @@ spec: - name: STORAGE_DRIVER value: $(params.STORAGE_DRIVER) volumeMounts: - - name: shared-dir - mountPath: /index-build-data + - mountPath: /index-build-data + name: shared-dir steps: - - image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c - # per https://kubernetes.io/docs/concepts/containers/images/#imagepullpolicy-defaulting - # the cluster will set imagePullPolicy to IfNotPresent - name: build + - args: + - $(params.IMAGES[*]) computeResources: limits: memory: 4Gi requests: - memory: 512Mi cpu: 250m - args: ["$(params.IMAGES[*])"] + memory: 512Mi + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build script: | #!/bin/bash # Fixing group permission on /var/lib/containers @@ -161,17 +163,16 @@ spec: securityContext: capabilities: add: - - SETFCAP - - - image: quay.io/konflux-ci/sbom-utility-scripts@sha256:7f4ce55033077f37eda61c67b4289949471c36d23ae0cc66fd4113e615b3ef93 - name: create-sbom - computeResources: + - SETFCAP + - computeResources: limits: - memory: 512Mi cpu: 200m + memory: 512Mi requests: - memory: 256Mi cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/sbom-utility-scripts@sha256:7f4ce55033077f37eda61c67b4289949471c36d23ae0cc66fd4113e615b3ef93 + name: create-sbom script: | #!/bin/bash set -e @@ -190,9 +191,15 @@ spec: --image-index-digest "$IMAGE_DIGEST" \ --inspect-input-file "$MANIFEST_DATA_FILE" \ --output-path /index-build-data/sbom-results.json - - - name: upload-sbom + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi image: quay.io/konflux-ci/appstudio-utils:48c311af02858e2422d6229600e9959e496ddef1@sha256:91ddd999271f65d8ec8487b10f3dd378f81aa894e11b9af4d10639fd52bba7e8 + name: upload-sbom script: | #!/bin/bash set -e @@ -210,10 +217,6 @@ spec: sbom_digest="$(sha256sum "$SBOM_RESULT_FILE" | cut -d' ' -f1)" # The SBOM_BLOB_URL is created by `cosign attach sbom`. echo -n "${sbom_repo}@sha256:${sbom_digest}" | tee "$(results.SBOM_BLOB_URL.path)" - computeResources: - limits: - memory: 512Mi - cpu: 200m - requests: - memory: 256Mi - cpu: 100m + volumes: + - emptyDir: {} + name: shared-dir diff --git a/task/build-image-manifest/0.1/build-image-manifest.yaml b/task/build-image-manifest/0.1/build-image-manifest.yaml new file mode 100644 index 0000000000..f609dce7bc --- /dev/null +++ b/task/build-image-manifest/0.1/build-image-manifest.yaml @@ -0,0 +1,222 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: "0.1" + build.appstudio.redhat.com/build_type: docker + name: build-image-manifest +spec: + description: This takes existing Image Manifests and combines them in an Image Index. + params: + - description: The target image and tag where the image will be pushed to. + name: IMAGE + type: string + - default: "true" + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) + name: TLSVERIFY + type: string + - default: "" + description: The commit the image is built from. + name: COMMIT_SHA + type: string + - description: List of Image Manifests to be referenced by the Image Index + name: IMAGES + type: array + - default: "" + description: Delete image tag after specified time resulting in garbage collection + of the digest. Empty means to keep the image tag. Time values could be something + like 1h, 2d, 3w for hours, days, and weeks, respectively. + name: IMAGE_EXPIRES_AFTER + type: string + - default: "true" + description: Build an image index even if IMAGES is of length 1. Default true. + If the image index generation is skipped, the task will forward values for params.IMAGES[0] + to results.IMAGE_*. In order to properly set all results, use the repository:tag@sha256:digest + format for the IMAGES parameter. + name: ALWAYS_BUILD_INDEX + type: string + - default: vfs + description: Storage driver to configure for buildah + name: STORAGE_DRIVER + type: string + results: + - description: Digest of the image just built + name: IMAGE_DIGEST + - description: Image repository and tag where the built image was pushed + name: IMAGE_URL + - description: List of all referenced image manifests + name: IMAGES + - description: Image reference of the built image containing both the repository + and the digest + name: IMAGE_REF + - description: Reference of SBOM blob digest to enable digest-based verification + from provenance + name: SBOM_BLOB_URL + type: string + stepTemplate: + env: + - name: BUILDAH_FORMAT + value: oci + - name: COMMIT_SHA + value: $(params.COMMIT_SHA) + - name: IMAGE + value: $(params.IMAGE) + - name: TLSVERIFY + value: $(params.TLSVERIFY) + - name: ALWAYS_BUILD_INDEX + value: $(params.ALWAYS_BUILD_INDEX) + - name: STORAGE_DRIVER + value: $(params.STORAGE_DRIVER) + volumeMounts: + - mountPath: /index-build-data + name: shared-dir + steps: + - args: + - $(params.IMAGES[*]) + computeResources: + limits: + memory: 4Gi + requests: + cpu: 250m + memory: 512Mi + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build + script: | + #!/bin/bash + # Fixing group permission on /var/lib/containers + set -eu + set -o pipefail + chown root:root /var/lib/containers + + sed -i 's/^\s*short-name-mode\s*=\s*.*/short-name-mode = "disabled"/' /etc/containers/registries.conf + + if [[ $# -ne 1 && "$ALWAYS_BUILD_INDEX" != "true" ]]; then + echo "Skipping image index generation while supplying multiple image inputs is unsupported." + exit 2 + fi + + buildah manifest create "$IMAGE" + for i in $@ + do + TOADD="$i" + TOADD_URL="$(echo "$i" | cut -d@ -f1)" + TOADD_DIGEST="$(echo "$i" | cut -d@ -f2)" + if [[ $(echo "$i" | tr -cd ":" | wc -c) == 2 ]]; then + #format is repository:tag@sha256:digest + #we need to remove the tag, and just reference the digest + #as tag + digest is not supported + TOADD_REPOSITORY="$(echo "$i" | cut -d: -f1)" + TOADD="${TOADD_REPOSITORY}@${TOADD_DIGEST}" + fi + if [[ "$ALWAYS_BUILD_INDEX" != "true" ]]; then + echo "Skipping image index generation. Returning results for $TOADD." + echo -n "${TOADD_URL}" > "$(results.IMAGE_URL.path)" + echo -n "${TOADD_DIGEST}" > "$(results.IMAGE_DIGEST.path)" + echo -n "${TOADD}" > "$(results.IMAGES.path)" + exit 0 + fi + echo "Adding $TOADD" + buildah manifest add $IMAGE "docker://$TOADD" --all + done + + status=-1 + max_run=5 + sleep_sec=10 + for run in $(seq 1 $max_run); do + status=0 + [ "$run" -gt 1 ] && sleep $sleep_sec + echo "Pushing image to registry" + buildah manifest push \ + --tls-verify=$TLSVERIFY \ + --digestfile image-digest $IMAGE \ + docker://$IMAGE && break || status=$? + done + if [ "$status" -ne 0 ]; then + echo "Failed to push image to registry after ${max_run} tries" + exit 1 + fi + + INDEX_REPOSITORY="$(echo "$IMAGE" | cut -d@ -f1 | cut -d: -f1)" + MANIFEST_DIGESTS=$(buildah manifest inspect "$IMAGE" | jq -er ".manifests[].digest") + image_manifests="" + for i in $MANIFEST_DIGESTS + do + image_manifests="${image_manifests} ${INDEX_REPOSITORY}@${i}," + done + + cat image-digest | tee $(results.IMAGE_DIGEST.path) + echo -n "$IMAGE" | tee "$(results.IMAGE_URL.path)" + { + echo -n "${IMAGE}@" + cat "image-digest" + } > "$(results.IMAGE_REF.path)" + echo -n "${image_manifests:1:-1}" > "$(results.IMAGES.path)" + + IMAGE_DIGEST=$(cat image-digest) + + INDEX_IMAGE_PULLSPEC="${IMAGE}@${IMAGE_DIGEST}" + buildah manifest inspect "$INDEX_IMAGE_PULLSPEC" > /index-build-data/manifest_data.json + securityContext: + capabilities: + add: + - SETFCAP + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/sbom-utility-scripts@sha256:7f4ce55033077f37eda61c67b4289949471c36d23ae0cc66fd4113e615b3ef93 + name: create-sbom + script: | + #!/bin/bash + set -e + + MANIFEST_DATA_FILE="/index-build-data/manifest_data.json" + if [ ! -f "$MANIFEST_DATA_FILE" ]; then + echo "The manifest_data.json file does not exist. Skipping the SBOM creation..." + exit 0 + fi + + IMAGE_URL="$(cat "$(results.IMAGE_URL.path)")" + IMAGE_DIGEST="$(cat "$(results.IMAGE_DIGEST.path)")" + echo "Creating SBOM result file..." + python3 index_image_sbom_script.py \ + --image-index-url "$IMAGE_URL" \ + --image-index-digest "$IMAGE_DIGEST" \ + --inspect-input-file "$MANIFEST_DATA_FILE" \ + --output-path /index-build-data/sbom-results.json + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/appstudio-utils:48c311af02858e2422d6229600e9959e496ddef1@sha256:91ddd999271f65d8ec8487b10f3dd378f81aa894e11b9af4d10639fd52bba7e8 + name: upload-sbom + script: | + #!/bin/bash + set -e + + SBOM_RESULT_FILE="/index-build-data/sbom-results.json" + if [ ! -f "$SBOM_RESULT_FILE" ]; then + echo "The sbom_results.json file does not exists. Skipping the SBOM upload..." + exit 0 + fi + + cosign attach sbom --sbom "$SBOM_RESULT_FILE" --type spdx "$(cat "$(results.IMAGE_REF.path)")" + + # Remove tag from IMAGE while allowing registry to contain a port number. + sbom_repo="${IMAGE%:*}" + sbom_digest="$(sha256sum "$SBOM_RESULT_FILE" | cut -d' ' -f1)" + # The SBOM_BLOB_URL is created by `cosign attach sbom`. + echo -n "${sbom_repo}@sha256:${sbom_digest}" | tee "$(results.SBOM_BLOB_URL.path)" + volumes: + - emptyDir: {} + name: shared-dir diff --git a/task/build-maven-zip-oci-ta/0.1/kustomization.yaml b/task/build-maven-zip-oci-ta/0.1/kustomization.yaml deleted file mode 100644 index 67a215d2a3..0000000000 --- a/task/build-maven-zip-oci-ta/0.1/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: -- build-maven-zip-oci-ta.yaml diff --git a/task/build-maven-zip/0.1/build-maven-zip.yaml b/task/build-maven-zip/0.1/build-maven-zip.yaml index 592324b48e..a18117e4bc 100644 --- a/task/build-maven-zip/0.1/build-maven-zip.yaml +++ b/task/build-maven-zip/0.1/build-maven-zip.yaml @@ -1,13 +1,13 @@ apiVersion: tekton.dev/v1 kind: Task metadata: - name: build-maven-zip annotations: - tekton.dev/pipelines.minVersion: "0.12.1" - tekton.dev/tags: "maven-build, konflux" + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: maven-build, konflux labels: app.kubernetes.io/version: "0.1" - build.appstudio.redhat.com/build_type: "maven-zip" + build.appstudio.redhat.com/build_type: maven-zip + name: build-maven-zip spec: description: |- Build-maven-zip task builds prefetched maven artifacts into a OCI-artifact with zip bundle and pushes the OCI-artifact into container registry. @@ -17,36 +17,41 @@ spec: - description: Reference of the OCI-Artifact this build task will produce. name: IMAGE type: string - - default: "maven-repository" - description: The root directory of the artifacts under the prefetched directory. Will be kept in the maven zip as the top directory for all artifacts. + - default: maven-repository + description: The root directory of the artifacts under the prefetched directory. + Will be kept in the maven zip as the top directory for all artifacts. name: PREFETCH_ROOT type: string - - default: "maven-repository" + - default: maven-repository description: The zip bundle file name of archived artifacts name: FILE_NAME type: string - default: "" - description: Delete image tag after specified time. Empty means to keep the image tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, respectively. + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. name: IMAGE_EXPIRES_AFTER type: string - - name: caTrustConfigMapName - type: string + - default: trusted-ca description: The name of the ConfigMap to read CA bundle data from. - default: trusted-ca - - name: caTrustConfigMapKey + name: caTrustConfigMapName + type: string + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey type: string - description: The name of the key in the ConfigMap that contains the CA bundle data. - default: ca-bundle.crt - results: - description: Digest of the OCI-Artifact just built name: IMAGE_DIGEST - - description: OCI-Artifact repository and tag where the built OCI-Artifact was pushed + - description: OCI-Artifact repository and tag where the built OCI-Artifact was + pushed name: IMAGE_URL - description: OCI-Artifact reference of the built OCI-Artifact name: IMAGE_REF - - name: SBOM_BLOB_URL - description: Reference of SBOM blob digest to enable digest-based verification from provenance + - description: Reference of SBOM blob digest to enable digest-based verification + from provenance + name: SBOM_BLOB_URL type: string stepTemplate: env: @@ -59,19 +64,18 @@ spec: - name: IMAGE_EXPIRES_AFTER value: $(params.IMAGE_EXPIRES_AFTER) volumeMounts: - - mountPath: /shared - name: shared - + - mountPath: /shared + name: shared steps: - - image: quay.io/konflux-ci/appstudio-utils@sha256:426143910a9fe57a340143f8c19f1ad8e7103749be84096c3faacc20b260b15a - name: prepare - computeResources: + - computeResources: limits: + cpu: "4" memory: 8Gi - cpu: '4' requests: + cpu: "1" memory: 2Gi - cpu: '1' + image: quay.io/konflux-ci/appstudio-utils@sha256:426143910a9fe57a340143f8c19f1ad8e7103749be84096c3faacc20b260b15a + name: prepare script: | #!/bin/bash set -euo pipefail @@ -100,17 +104,17 @@ spec: securityContext: capabilities: add: - - SETFCAP + - SETFCAP workingDir: $(workspaces.source.path) - - image: quay.io/konflux-ci/oras:latest@sha256:66ccc8c3698304036a42739f6e1836f3399a46645be2d3c5d6d456b9c79fff40 - name: build - computeResources: + - computeResources: limits: + cpu: "4" memory: 8Gi - cpu: '4' requests: + cpu: "1" memory: 2Gi - cpu: '1' + image: quay.io/konflux-ci/oras:latest@sha256:66ccc8c3698304036a42739f6e1836f3399a46645be2d3c5d6d456b9c79fff40 + name: build script: | #!/bin/bash set -euo pipefail @@ -141,14 +145,21 @@ spec: securityContext: capabilities: add: - - SETFCAP + - SETFCAP volumeMounts: - - name: trusted-ca - mountPath: /mnt/trusted-ca + - mountPath: /mnt/trusted-ca + name: trusted-ca readOnly: true workingDir: $(workspaces.source.path) - - name: upload-sbom + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi image: quay.io/konflux-ci/appstudio-utils:ab6b0b8e40e440158e7288c73aff1cf83a2cc8a9@sha256:24179f0efd06c65d16868c2d7eb82573cce8e43533de6cea14fec3b7446e0b14 + name: upload-sbom script: | #!/bin/bash set -euo pipefail @@ -166,29 +177,21 @@ spec: sbom_tag="sha256-$(< "$(results.IMAGE_DIGEST.path)" cut -d: -f2).sbom" # The SBOM_BLOB_URL is created by `cosign attach sbom`. echo -n "${sbom_repo}:${sbom_tag}" | tee "$(results.SBOM_BLOB_URL.path)" - computeResources: - limits: - memory: 512Mi - cpu: 200m - requests: - memory: 256Mi - cpu: 100m volumeMounts: - - name: trusted-ca - mountPath: /mnt/trusted-ca + - mountPath: /mnt/trusted-ca + name: trusted-ca readOnly: true workingDir: $(workspaces.source.path) - volumes: - - name: shared - emptyDir: {} - - name: trusted-ca - configMap: - name: $(params.caTrustConfigMapName) + - emptyDir: {} + name: shared + - configMap: items: - - key: $(params.caTrustConfigMapKey) - path: ca-bundle.crt + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) optional: true + name: trusted-ca workspaces: - - name: source - description: Workspace containing the source code to build. + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah-10gb/0.1/buildah-10gb.yaml b/task/buildah-10gb/0.1/buildah-10gb.yaml new file mode 100644 index 0000000000..03929e53d9 --- /dev/null +++ b/task/buildah-10gb/0.1/buildah-10gb.yaml @@ -0,0 +1,585 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2025-01-21T00:00:00Z" + build.appstudio.redhat.com/expiry-message: 'Instead of using this task, switch + to buildah task (you can find latest bundle reference here: https://quay.io/repository/konflux-ci/tekton-catalog/task-buildah) + and configure build step resources as described in the doc: https://konflux-ci.dev/docs/how-tos/configuring/overriding-compute-resources/' + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: "0.1" + build.appstudio.redhat.com/build_type: docker + name: buildah-10gb +spec: + description: |- + Buildah task builds source code into a container image and pushes the image into container registry using buildah tool. + In addition it generates a SBOM file, injects the SBOM file into final container image and pushes the SBOM file as separate image using cosign tool. + When [Java dependency rebuild](https://redhat-appstudio.github.io/docs.stonesoup.io/Documentation/main/cli/proc_enabled_java_dependencies.html) is enabled it triggers rebuilds of Java artifacts. + When prefetch-dependencies task was activated it is using its artifacts to run build in hermetic environment. + params: + - description: Reference of the image buildah will produce. + name: IMAGE + type: string + - default: "" + description: Deprecated. Has no effect. Will be removed in the future. + name: BUILDER_IMAGE + type: string + - default: ./Dockerfile + description: Path to the Dockerfile to build. + name: DOCKERFILE + type: string + - default: . + description: Path to the directory to use as context. + name: CONTEXT + type: string + - default: "true" + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) + name: TLSVERIFY + type: string + - default: "" + description: unused, should be removed in next task version + name: DOCKER_AUTH + type: string + - default: "false" + description: Determines if build will be executed without network access. + name: HERMETIC + type: string + - default: "" + description: In case it is not empty, the prefetched content should be made available + to the build. + name: PREFETCH_INPUT + type: string + - default: "" + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. + name: IMAGE_EXPIRES_AFTER + type: string + - default: "" + description: The image is built from this commit. + name: COMMIT_SHA + type: string + - default: repos.d + description: Path in the git repository in which yum repository files are stored + name: YUM_REPOS_D_SRC + - default: fetched.repos.d + description: Path in source workspace where dynamically-fetched repos are present + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE + type: string + - default: etc-pki-entitlement + description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET + type: string + - default: activation-key + description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY + type: string + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS + type: array + - default: "" + description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE + type: string + - default: trusted-ca + description: The name of the ConfigMap to read CA bundle data from. + name: caTrustConfigMapName + type: string + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey + type: string + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES + type: string + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH + type: string + - default: vfs + description: Storage driver to configure for buildah + name: STORAGE_DRIVER + type: string + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES + type: string + results: + - description: Digest of the image just built + name: IMAGE_DIGEST + - description: Image repository and tag where the built image was pushed + name: IMAGE_URL + - description: Digests of the base images used for build + name: BASE_IMAGES_DIGESTS + - description: The counting of Java components by publisher in JSON format + name: SBOM_JAVA_COMPONENTS_COUNT + type: string + - description: The Java dependencies that came from community sources such as Maven + central. + name: JAVA_COMMUNITY_DEPENDENCIES + stepTemplate: + env: + - name: BUILDAH_FORMAT + value: oci + - name: STORAGE_DRIVER + value: $(params.STORAGE_DRIVER) + - name: HERMETIC + value: $(params.HERMETIC) + - name: CONTEXT + value: $(params.CONTEXT) + - name: DOCKERFILE + value: $(params.DOCKERFILE) + - name: IMAGE + value: $(params.IMAGE) + - name: TLSVERIFY + value: $(params.TLSVERIFY) + - name: IMAGE_EXPIRES_AFTER + value: $(params.IMAGE_EXPIRES_AFTER) + - name: YUM_REPOS_D_SRC + value: $(params.YUM_REPOS_D_SRC) + - name: YUM_REPOS_D_FETCHED + value: $(params.YUM_REPOS_D_FETCHED) + - name: YUM_REPOS_D_TARGET + value: $(params.YUM_REPOS_D_TARGET) + - name: TARGET_STAGE + value: $(params.TARGET_STAGE) + - name: PARAM_BUILDER_IMAGE + value: $(params.BUILDER_IMAGE) + - name: ENTITLEMENT_SECRET + value: $(params.ENTITLEMENT_SECRET) + - name: ADDITIONAL_SECRET + value: $(params.ADDITIONAL_SECRET) + - name: BUILD_ARGS_FILE + value: $(params.BUILD_ARGS_FILE) + - name: ADD_CAPABILITIES + value: $(params.ADD_CAPABILITIES) + - name: SQUASH + value: $(params.SQUASH) + - name: SKIP_UNUSED_STAGES + value: $(params.SKIP_UNUSED_STAGES) + volumeMounts: + - mountPath: /shared + name: shared + steps: + - args: + - $(params.BUILD_ARGS[*]) + computeResources: + limits: + memory: 10Gi + requests: + cpu: 250m + memory: 8Gi + env: + - name: COMMIT_SHA + value: $(params.COMMIT_SHA) + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build + script: | + if [ -n "${PARAM_BUILDER_IMAGE}" ]; then + echo "WARNING: provided deprecated BUILDER_IMAGE parameter has no effect." + fi + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + SOURCE_CODE_DIR=source + if [ -e "$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" + elif [ -e "$SOURCE_CODE_DIR/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$DOCKERFILE" + elif echo "$DOCKERFILE" | grep -q "^https\?://"; then + echo "Fetch Dockerfile from $DOCKERFILE" + dockerfile_path=$(mktemp --suffix=-Dockerfile) + http_code=$(curl -s -L -w "%{http_code}" --output "$dockerfile_path" "$DOCKERFILE") + if [ $http_code != 200 ]; then + echo "No Dockerfile is fetched. Server responds $http_code" + exit 1 + fi + http_code=$(curl -s -L -w "%{http_code}" --output "$dockerfile_path.dockerignore.tmp" "$DOCKERFILE.dockerignore") + if [ $http_code = 200 ]; then + echo "Fetched .dockerignore from $DOCKERFILE.dockerignore" + mv "$dockerfile_path.dockerignore.tmp" $SOURCE_CODE_DIR/$CONTEXT/.dockerignore + fi + else + echo "Cannot find Dockerfile $DOCKERFILE" + exit 1 + fi + if [ -n "$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR" ] && grep -q '^\s*RUN \(./\)\?mvn' "$dockerfile_path"; then + sed -i -e "s|^\s*RUN \(\(./\)\?mvn\)\(.*\)|RUN echo \"mirror.defaulthttp://$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR/v1/cache/default/0/*\" > /tmp/settings.yaml; \1 -s /tmp/settings.yaml \3|g" "$dockerfile_path" + touch /var/lib/containers/java + fi + + # Fixing group permission on /var/lib/containers + chown root:root /var/lib/containers + + sed -i 's/^\s*short-name-mode\s*=\s*.*/short-name-mode = "disabled"/' /etc/containers/registries.conf + + # Setting new namespace to run buildah - 2^32-2 + echo 'root:1:4294967294' | tee -a /etc/subuid >> /etc/subgid + + BUILDAH_ARGS=() + + BASE_IMAGES=$(grep -i '^\s*FROM' "$dockerfile_path" | sed 's/--platform=\S*//' | awk '{print $2}' | (grep -v ^oci-archive: || true)) + if [ "${HERMETIC}" == "true" ]; then + BUILDAH_ARGS+=("--pull=never") + UNSHARE_ARGS="--net" + for image in $BASE_IMAGES; do + if [ "${image}" != "scratch" ]; then + unshare -Ufp --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -- buildah pull $image + fi + done + echo "Build will be executed with network isolation" + fi + + if [ -n "${TARGET_STAGE}" ]; then + BUILDAH_ARGS+=("--target=${TARGET_STAGE}") + fi + + if [ -n "${BUILD_ARGS_FILE}" ]; then + BUILDAH_ARGS+=("--build-arg-file=$(pwd)/$SOURCE_CODE_DIR/${BUILD_ARGS_FILE}") + fi + + for build_arg in "$@"; do + BUILDAH_ARGS+=("--build-arg=$build_arg") + done + + if [ -n "${ADD_CAPABILITIES}" ]; then + BUILDAH_ARGS+=("--cap-add=${ADD_CAPABILITIES}") + fi + + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + if [ "${SKIP_UNUSED_STAGES}" != "true" ] ; then + BUILDAH_ARGS+=("--skip-unused-stages=false") + fi + + if [ -f "$(workspaces.source.path)/cachi2/cachi2.env" ]; then + cp -r "$(workspaces.source.path)/cachi2" /tmp/ + chmod -R go+rwX /tmp/cachi2 + VOLUME_MOUNTS="--volume /tmp/cachi2:/cachi2" + # Read in the whole file (https://unix.stackexchange.com/questions/533277), then + # for each RUN ... line insert the cachi2.env command *after* any options like --mount + sed -E -i \ + -e 'H;1h;$!d;x' \ + -e 's@^\s*(run((\s|\\\n)+-\S+)*(\s|\\\n)+)@\1. /cachi2/cachi2.env \&\& \\\n @igM' \ + "$dockerfile_path" + echo "Prefetched content will be made available" + + prefetched_repo_for_my_arch="/tmp/cachi2/output/deps/rpm/$(uname -m)/repos.d/cachi2.repo" + if [ -f "$prefetched_repo_for_my_arch" ]; then + echo "Adding $prefetched_repo_for_my_arch to $YUM_REPOS_D_FETCHED" + mkdir -p "$YUM_REPOS_D_FETCHED" + cp --no-clobber "$prefetched_repo_for_my_arch" "$YUM_REPOS_D_FETCHED" + fi + fi + + # if yum repofiles stored in git, copy them to mount point outside the source dir + if [ -d "${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}" ]; then + mkdir -p ${YUM_REPOS_D_FETCHED} + cp -r ${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}/* ${YUM_REPOS_D_FETCHED} + fi + + # if anything in the repofiles mount point (either fetched or from git), mount it + if [ -d "${YUM_REPOS_D_FETCHED}" ]; then + chmod -R go+rwX ${YUM_REPOS_D_FETCHED} + mount_point=$(realpath ${YUM_REPOS_D_FETCHED}) + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume ${mount_point}:${YUM_REPOS_D_TARGET}" + fi + + LABELS=( + "--label" "build-date=$(date -u +'%Y-%m-%dT%H:%M:%S')" + "--label" "architecture=$(uname -m)" + "--label" "vcs-type=git" + ) + [ -n "$COMMIT_SHA" ] && LABELS+=("--label" "vcs-ref=$COMMIT_SHA") + [ -n "$IMAGE_EXPIRES_AFTER" ] && LABELS+=("--label" "quay.expires-after=$IMAGE_EXPIRES_AFTER") + + ACTIVATION_KEY_PATH="/activation-key" + ENTITLEMENT_PATH="/entitlement" + + if [ -e /activation-key/org ]; then + cp -r --preserve=mode "$ACTIVATION_KEY_PATH" /tmp/activation-key + mkdir /shared/rhsm-tmp + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume /tmp/activation-key:/activation-key -v /shared/rhsm-tmp:/etc/pki/entitlement:Z" + echo "Adding activation key to the build" + + elif find /entitlement -name "*.pem" >> null; then + cp -r --preserve=mode "$ENTITLEMENT_PATH" /tmp/entitlement + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume /tmp/entitlement:/etc/pki/entitlement" + echo "Adding the entitlement to the build" + fi + + ADDITIONAL_SECRET_PATH="/additional-secret" + ADDITIONAL_SECRET_TMP="/tmp/additional-secret" + if [ -d "$ADDITIONAL_SECRET_PATH" ]; then + cp -r --preserve=mode -L "$ADDITIONAL_SECRET_PATH" $ADDITIONAL_SECRET_TMP + while read -r filename; do + echo "Adding the secret ${ADDITIONAL_SECRET}/${filename} to the build, available at /run/secrets/${ADDITIONAL_SECRET}/${filename}" + BUILDAH_ARGS+=("--secret=id=${ADDITIONAL_SECRET}/${filename},src=$ADDITIONAL_SECRET_TMP/${filename}") + done < <(find $ADDITIONAL_SECRET_TMP -maxdepth 1 -type f -exec basename {} \;) + fi + + unshare -Uf $UNSHARE_ARGS --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -w ${SOURCE_CODE_DIR}/$CONTEXT -- buildah build \ + $VOLUME_MOUNTS \ + "${BUILDAH_ARGS[@]}" \ + "${LABELS[@]}" \ + --tls-verify=$TLSVERIFY --no-cache \ + --ulimit nofile=4096:4096 \ + -f "$dockerfile_path" -t $IMAGE . + + container=$(buildah from --pull-never $IMAGE) + buildah mount $container | tee /shared/container_path + # delete symlinks - they may point outside the container rootfs, messing with SBOM scanners + find $(cat /shared/container_path) -xtype l -delete + echo $container > /shared/container_name + + # Save the SBOM produced by Cachi2 so it can be merged into the final SBOM later + if [ -f "/tmp/cachi2/output/bom.json" ]; then + cp /tmp/cachi2/output/bom.json ./sbom-cachi2.json + fi + + # Expose base image digests + touch $(results.BASE_IMAGES_DIGESTS.path) + for image in $BASE_IMAGES; do + if [ "${image}" != "scratch" ]; then + buildah images --format '{{ .Name }}:{{ .Tag }}@{{ .Digest }}' --filter reference="$image" >> $(results.BASE_IMAGES_DIGESTS.path) + fi + done + + # Needed to generate base images SBOM + echo "$BASE_IMAGES" > $(workspaces.source.path)/base_images_from_dockerfile + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /entitlement + name: etc-pki-entitlement + - mountPath: /activation-key + name: activation-key + - mountPath: /additional-secret + name: additional-secret + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate + script: | + echo "Running syft on the source directory" + syft dir:$(workspaces.source.path)/source --output cyclonedx-json=$(workspaces.source.path)/sbom-source.json + echo "Running syft on the image filesystem" + syft dir:$(cat /shared/container_path) --output cyclonedx-json=$(workspaces.source.path)/sbom-image.json + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + workingDir: $(workspaces.source.path)/source + - image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + name: analyse-dependencies-java-sbom + script: | + if [ -f /var/lib/containers/java ]; then + /opt/jboss/container/java/run/run-java.sh analyse-dependencies path $(cat /shared/container_path) -s $(workspaces.source.path)/sbom-image.json --task-run-name $(context.taskRun.name) --publishers $(results.SBOM_JAVA_COMPONENTS_COUNT.path) + sed -i 's/^/ /' $(results.SBOM_JAVA_COMPONENTS_COUNT.path) # Workaround for SRVKP-2875 + else + touch $(results.JAVA_COMMUNITY_DEPENDENCIES.path) + fi + securityContext: + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + name: merge-syft-sboms + script: | + #!/bin/python3 + import json + + # load SBOMs + with open("./sbom-image.json") as f: + image_sbom = json.load(f) + + with open("./sbom-source.json") as f: + source_sbom = json.load(f) + + # fetch unique components from available SBOMs + def get_identifier(component): + return component["name"] + '@' + component.get("version", "") + + image_sbom_components = image_sbom.setdefault("components", []) + existing_components = [get_identifier(component) for component in image_sbom_components] + + source_sbom_components = source_sbom.get("components", []) + for component in source_sbom_components: + if get_identifier(component) not in existing_components: + image_sbom_components.append(component) + existing_components.append(get_identifier(component)) + + image_sbom_components.sort(key=lambda c: get_identifier(c)) + + # write the CycloneDX unified SBOM + with open("./sbom-cyclonedx.json", "w") as f: + json.dump(image_sbom, f, indent=4) + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/cachi2:0.17.0@sha256:963870f04aeb4a207e79b8eacb47e16c8faa7451f52e92959e0e16cdbd258fb3 + name: merge-cachi2-sbom + script: | + if [ -f "sbom-cachi2.json" ]; then + echo "Merging contents of sbom-cachi2.json into sbom-cyclonedx.json" + merge_syft_sbom sbom-cachi2.json sbom-cyclonedx.json > sbom-temp.json + mv sbom-temp.json sbom-cyclonedx.json + else + echo "Skipping step since no Cachi2 SBOM was produced" + fi + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + name: create-purl-sbom + script: | + #!/bin/python3 + import json + + with open("./sbom-cyclonedx.json") as f: + cyclonedx_sbom = json.load(f) + + purls = [{"purl": component["purl"]} for component in cyclonedx_sbom.get("components", []) if "purl" in component] + purl_content = {"image_contents": {"dependencies": purls}} + + with open("sbom-purl.json", "w") as output_file: + json.dump(purl_content, output_file, indent=4) + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - env: + - name: BASE_IMAGES_DIGESTS_PATH + value: $(results.BASE_IMAGES_DIGESTS.path) + image: quay.io/redhat-appstudio/base-images-sbom-script@sha256:667669e3def018f9dbb8eaf8868887a40bc07842221e9a98f6787edcff021840 + name: create-base-images-sbom + script: | + python3 /app/base_images_sbom_script.py --sbom=sbom-cyclonedx.json --base-images-from-dockerfile=base_images_from_dockerfile --base-images-digests=$BASE_IMAGES_DIGESTS_PATH + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - computeResources: {} + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: inject-sbom-and-push + script: | + if [ -n "${PARAM_BUILDER_IMAGE}" ]; then + echo "WARNING: provided deprecated BUILDER_IMAGE parameter has no effect." + fi + + base_image_name=$(buildah inspect --format '{{ index .ImageAnnotations "org.opencontainers.image.base.name"}}' $IMAGE | cut -f1 -d'@') + base_image_digest=$(buildah inspect --format '{{ index .ImageAnnotations "org.opencontainers.image.base.digest"}}' $IMAGE) + container=$(buildah from --pull-never $IMAGE) + buildah copy $container sbom-cyclonedx.json sbom-purl.json /root/buildinfo/content_manifests/ + buildah config -a org.opencontainers.image.base.name=${base_image_name} -a org.opencontainers.image.base.digest=${base_image_digest} $container + + BUILDAH_ARGS=() + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + buildah commit "${BUILDAH_ARGS[@]}" $container $IMAGE + + status=-1 + max_run=5 + sleep_sec=10 + for run in $(seq 1 $max_run); do + status=0 + [ "$run" -gt 1 ] && sleep $sleep_sec + echo "Pushing sbom image to registry" + buildah push \ + --tls-verify=$TLSVERIFY \ + --digestfile $(workspaces.source.path)/image-digest $IMAGE \ + docker://$IMAGE && break || status=$? + done + if [ "$status" -ne 0 ]; then + echo "Failed to push sbom image to registry after ${max_run} tries" + exit 1 + fi + + cat "$(workspaces.source.path)"/image-digest | tee $(results.IMAGE_DIGEST.path) + echo -n "$IMAGE" | tee $(results.IMAGE_URL.path) + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + workingDir: $(workspaces.source.path) + - args: + - attach + - sbom + - --sbom + - sbom-cyclonedx.json + - --type + - cyclonedx + - $(params.IMAGE) + image: quay.io/redhat-appstudio/cosign:v2.1.1@sha256:c883d6f8d39148f2cea71bff4622d196d89df3e510f36c140c097b932f0dd5d5 + name: upload-sbom + workingDir: $(workspaces.source.path) + volumes: + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared + - name: etc-pki-entitlement + secret: + optional: true + secretName: $(params.ENTITLEMENT_SECRET) + - name: activation-key + secret: + optional: true + secretName: $(params.ACTIVATION_KEY) + - name: additional-secret + secret: + optional: true + secretName: $(params.ADDITIONAL_SECRET) + - configMap: + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) + optional: true + name: trusted-ca + workspaces: + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah-10gb/0.2/buildah-10gb.yaml b/task/buildah-10gb/0.2/buildah-10gb.yaml new file mode 100644 index 0000000000..8b03006af3 --- /dev/null +++ b/task/buildah-10gb/0.2/buildah-10gb.yaml @@ -0,0 +1,742 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2025-01-21T00:00:00Z" + build.appstudio.redhat.com/expiry-message: 'Instead of using this task, switch + to buildah task (you can find latest bundle reference here: https://quay.io/repository/konflux-ci/tekton-catalog/task-buildah) + and configure build step resources as described in the doc: https://konflux-ci.dev/docs/how-tos/configuring/overriding-compute-resources/' + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: 0.2.1 + build.appstudio.redhat.com/build_type: docker + name: buildah-10gb +spec: + description: |- + Buildah task builds source code into a container image and pushes the image into container registry using buildah tool. + In addition it generates a SBOM file, injects the SBOM file into final container image and pushes the SBOM file as separate image using cosign tool. + When [Java dependency rebuild](https://redhat-appstudio.github.io/docs.stonesoup.io/Documentation/main/cli/proc_enabled_java_dependencies.html) is enabled it triggers rebuilds of Java artifacts. + When prefetch-dependencies task was activated it is using its artifacts to run build in hermetic environment. + params: + - description: Reference of the image buildah will produce. + name: IMAGE + type: string + - default: ./Dockerfile + description: Path to the Dockerfile to build. + name: DOCKERFILE + type: string + - default: . + description: Path to the directory to use as context. + name: CONTEXT + type: string + - default: "true" + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) + name: TLSVERIFY + type: string + - default: "false" + description: Determines if build will be executed without network access. + name: HERMETIC + type: string + - default: "" + description: In case it is not empty, the prefetched content should be made available + to the build. + name: PREFETCH_INPUT + type: string + - default: "" + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. + name: IMAGE_EXPIRES_AFTER + type: string + - default: "" + description: The image is built from this commit. + name: COMMIT_SHA + type: string + - default: repos.d + description: Path in the git repository in which yum repository files are stored + name: YUM_REPOS_D_SRC + - default: fetched.repos.d + description: Path in source workspace where dynamically-fetched repos are present + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE + type: string + - default: etc-pki-entitlement + description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET + type: string + - default: activation-key + description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY + type: string + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS + type: array + - default: "" + description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE + type: string + - default: trusted-ca + description: The name of the ConfigMap to read CA bundle data from. + name: caTrustConfigMapName + type: string + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey + type: string + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES + type: string + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH + type: string + - default: vfs + description: Storage driver to configure for buildah + name: STORAGE_DRIVER + type: string + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES + type: string + - default: [] + description: Additional key=value labels that should be applied to the image + name: LABELS + type: array + - default: "false" + description: Whether to enable privileged mode + name: PRIVILEGED_NESTED + type: string + - default: "false" + description: Skip SBOM-related operations. This will likely cause EC policies + to fail if enabled + name: SKIP_SBOM_GENERATION + type: string + results: + - description: Digest of the image just built + name: IMAGE_DIGEST + - description: Image repository and tag where the built image was pushed + name: IMAGE_URL + - description: Image reference of the built image + name: IMAGE_REF + - description: Reference of SBOM blob digest to enable digest-based verification + from provenance + name: SBOM_BLOB_URL + type: string + - description: The counting of Java components by publisher in JSON format + name: SBOM_JAVA_COMPONENTS_COUNT + type: string + - description: The Java dependencies that came from community sources such as Maven + central. + name: JAVA_COMMUNITY_DEPENDENCIES + stepTemplate: + computeResources: + limits: + cpu: "4" + memory: 4Gi + requests: + cpu: "1" + memory: 1Gi + env: + - name: BUILDAH_FORMAT + value: oci + - name: STORAGE_DRIVER + value: $(params.STORAGE_DRIVER) + - name: HERMETIC + value: $(params.HERMETIC) + - name: SOURCE_CODE_DIR + value: source + - name: CONTEXT + value: $(params.CONTEXT) + - name: IMAGE + value: $(params.IMAGE) + - name: TLSVERIFY + value: $(params.TLSVERIFY) + - name: IMAGE_EXPIRES_AFTER + value: $(params.IMAGE_EXPIRES_AFTER) + - name: YUM_REPOS_D_SRC + value: $(params.YUM_REPOS_D_SRC) + - name: YUM_REPOS_D_FETCHED + value: $(params.YUM_REPOS_D_FETCHED) + - name: YUM_REPOS_D_TARGET + value: $(params.YUM_REPOS_D_TARGET) + - name: TARGET_STAGE + value: $(params.TARGET_STAGE) + - name: ENTITLEMENT_SECRET + value: $(params.ENTITLEMENT_SECRET) + - name: ACTIVATION_KEY + value: $(params.ACTIVATION_KEY) + - name: ADDITIONAL_SECRET + value: $(params.ADDITIONAL_SECRET) + - name: BUILD_ARGS_FILE + value: $(params.BUILD_ARGS_FILE) + - name: ADD_CAPABILITIES + value: $(params.ADD_CAPABILITIES) + - name: SQUASH + value: $(params.SQUASH) + - name: SKIP_UNUSED_STAGES + value: $(params.SKIP_UNUSED_STAGES) + - name: PRIVILEGED_NESTED + value: $(params.PRIVILEGED_NESTED) + - name: SKIP_SBOM_GENERATION + value: $(params.SKIP_SBOM_GENERATION) + volumeMounts: + - mountPath: /shared + name: shared + steps: + - args: + - --build-args + - $(params.BUILD_ARGS[*]) + - --labels + - $(params.LABELS[*]) + computeResources: + limits: + cpu: "4" + memory: 10Gi + requests: + cpu: "1" + memory: 8Gi + env: + - name: COMMIT_SHA + value: $(params.COMMIT_SHA) + - name: DOCKERFILE + value: $(params.DOCKERFILE) + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build + script: | + #!/bin/bash + set -euo pipefail + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + if [ -e "$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" + elif [ -e "$SOURCE_CODE_DIR/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$DOCKERFILE" + elif [ -e "$DOCKERFILE" ]; then + # Instrumented builds (SAST) use this custom dockerffile step as their base + dockerfile_path="$DOCKERFILE" + elif echo "$DOCKERFILE" | grep -q "^https\?://"; then + echo "Fetch Dockerfile from $DOCKERFILE" + dockerfile_path=$(mktemp --suffix=-Dockerfile) + http_code=$(curl -s -S -L -w "%{http_code}" --output "$dockerfile_path" "$DOCKERFILE") + if [ $http_code != 200 ]; then + echo "No Dockerfile is fetched. Server responds $http_code" + exit 1 + fi + http_code=$(curl -s -S -L -w "%{http_code}" --output "$dockerfile_path.dockerignore.tmp" "$DOCKERFILE.dockerignore") + if [ $http_code = 200 ]; then + echo "Fetched .dockerignore from $DOCKERFILE.dockerignore" + mv "$dockerfile_path.dockerignore.tmp" $SOURCE_CODE_DIR/$CONTEXT/.dockerignore + fi + else + echo "Cannot find Dockerfile $DOCKERFILE" + exit 1 + fi + + dockerfile_copy=$(mktemp --tmpdir "$(basename "$dockerfile_path").XXXXXX") + cp "$dockerfile_path" "$dockerfile_copy" + + if [ -n "${JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR-}" ] && grep -q '^\s*RUN \(./\)\?mvn' "$dockerfile_copy"; then + sed -i -e "s|^\s*RUN \(\(./\)\?mvn\)\(.*\)|RUN echo \"mirror.defaulthttp://$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR/v1/cache/default/0/*\" > /tmp/settings.yaml; \1 -s /tmp/settings.yaml \3|g" "$dockerfile_copy" + touch /var/lib/containers/java + fi + + # Fixing group permission on /var/lib/containers + chown root:root /var/lib/containers + + sed -i 's/^\s*short-name-mode\s*=\s*.*/short-name-mode = "disabled"/' /etc/containers/registries.conf + + # Setting new namespace to run buildah - 2^32-2 + echo 'root:1:4294967294' | tee -a /etc/subuid >> /etc/subgid + + build_args=() + if [ -n "${BUILD_ARGS_FILE}" ]; then + # Parse BUILD_ARGS_FILE ourselves because dockerfile-json doesn't support it + echo "Parsing ARGs from $BUILD_ARGS_FILE" + mapfile -t build_args < <( + # https://www.mankier.com/1/buildah-build#--build-arg-file + # delete lines that start with # + # delete blank lines + sed -e '/^#/d' -e '/^\s*$/d' "${SOURCE_CODE_DIR}/${BUILD_ARGS_FILE}" + ) + fi + + LABELS=() + # Split `args` into two sets of arguments. + while [[ $# -gt 0 ]]; do + case $1 in + --build-args) + shift + # Note: this may result in multiple --build-arg=KEY=value flags with the same KEY being + # passed to buildah. In that case, the *last* occurrence takes precedence. This is why + # we append BUILD_ARGS after the content of the BUILD_ARGS_FILE + while [[ $# -gt 0 && $1 != --* ]]; do build_args+=("$1"); shift; done + ;; + --labels) + shift + while [[ $# -gt 0 && $1 != --* ]]; do LABELS+=("--label" "$1"); shift; done + ;; + *) + echo "unexpected argument: $1" >&2 + exit 2 + ;; + esac + done + + BUILD_ARG_FLAGS=() + for build_arg in "${build_args[@]}"; do + BUILD_ARG_FLAGS+=("--build-arg=$build_arg") + done + + dockerfile-json "${BUILD_ARG_FLAGS[@]}" "$dockerfile_copy" > /shared/parsed_dockerfile.json + BASE_IMAGES=$( + jq -r '.Stages[] | select(.From | .Stage or .Scratch | not) | .BaseName | select(test("^oci-archive:") | not)' /shared/parsed_dockerfile.json + ) + + BUILDAH_ARGS=() + UNSHARE_ARGS=() + + if [ "${HERMETIC}" == "true" ]; then + BUILDAH_ARGS+=("--pull=never") + UNSHARE_ARGS+=("--net") + + for image in $BASE_IMAGES; do + unshare -Ufp --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -- buildah pull $image + done + echo "Build will be executed with network isolation" + fi + + if [ -n "${TARGET_STAGE}" ]; then + BUILDAH_ARGS+=("--target=${TARGET_STAGE}") + fi + + BUILDAH_ARGS+=("${BUILD_ARG_FLAGS[@]}") + + if [ "${PRIVILEGED_NESTED}" == "true" ]; then + BUILDAH_ARGS+=("--security-opt=label=disable") + BUILDAH_ARGS+=("--cap-add=all") + BUILDAH_ARGS+=("--device=/dev/fuse") + fi + + if [ -n "${ADD_CAPABILITIES}" ]; then + BUILDAH_ARGS+=("--cap-add=${ADD_CAPABILITIES}") + fi + + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + if [ "${SKIP_UNUSED_STAGES}" != "true" ] ; then + BUILDAH_ARGS+=("--skip-unused-stages=false") + fi + + VOLUME_MOUNTS=() + + if [ -f "$(workspaces.source.path)/cachi2/cachi2.env" ]; then + cp -r "$(workspaces.source.path)/cachi2" /tmp/ + chmod -R go+rwX /tmp/cachi2 + VOLUME_MOUNTS+=(--volume /tmp/cachi2:/cachi2) + # Read in the whole file (https://unix.stackexchange.com/questions/533277), then + # for each RUN ... line insert the cachi2.env command *after* any options like --mount + sed -E -i \ + -e 'H;1h;$!d;x' \ + -e 's@^\s*(run((\s|\\\n)+-\S+)*(\s|\\\n)+)@\1. /cachi2/cachi2.env \&\& \\\n @igM' \ + "$dockerfile_copy" + echo "Prefetched content will be made available" + + prefetched_repo_for_my_arch="/tmp/cachi2/output/deps/rpm/$(uname -m)/repos.d/cachi2.repo" + if [ -f "$prefetched_repo_for_my_arch" ]; then + echo "Adding $prefetched_repo_for_my_arch to $YUM_REPOS_D_FETCHED" + mkdir -p "$YUM_REPOS_D_FETCHED" + cp --no-clobber "$prefetched_repo_for_my_arch" "$YUM_REPOS_D_FETCHED" + fi + fi + + # if yum repofiles stored in git, copy them to mount point outside the source dir + if [ -d "${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}" ]; then + mkdir -p ${YUM_REPOS_D_FETCHED} + cp -r ${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}/* ${YUM_REPOS_D_FETCHED} + fi + + # if anything in the repofiles mount point (either fetched or from git), mount it + if [ -d "${YUM_REPOS_D_FETCHED}" ]; then + chmod -R go+rwX ${YUM_REPOS_D_FETCHED} + mount_point=$(realpath ${YUM_REPOS_D_FETCHED}) + VOLUME_MOUNTS+=(--volume "${mount_point}:${YUM_REPOS_D_TARGET}") + fi + + DEFAULT_LABELS=( + "--label" "build-date=$(date -u +'%Y-%m-%dT%H:%M:%S')" + "--label" "architecture=$(uname -m)" + "--label" "vcs-type=git" + ) + [ -n "$COMMIT_SHA" ] && DEFAULT_LABELS+=("--label" "vcs-ref=$COMMIT_SHA") + [ -n "$IMAGE_EXPIRES_AFTER" ] && DEFAULT_LABELS+=("--label" "quay.expires-after=$IMAGE_EXPIRES_AFTER") + + # Concatenate defaults and explicit labels. If a label appears twice, the last one wins. + LABELS=("${DEFAULT_LABELS[@]}" "${LABELS[@]}") + + ACTIVATION_KEY_PATH="/activation-key" + ENTITLEMENT_PATH="/entitlement" + + # 0. if hermetic=true, skip all subscription related stuff + # 1. do not enable activation key and entitlement at same time. If both vars are provided, prefer activation key. + # 2. Activation-keys will be used when the key 'org' exists in the activation key secret. + # 3. try to pre-register and mount files to the correct location so that users do no need to modify Dockerfiles. + # 3. If the Dockerfile contains the string "subcription-manager register", add the activation-keys volume + # to buildah but don't pre-register for backwards compatibility. Mount an empty directory on + # shared emptydir volume to "/etc/pki/entitlement" to prevent certificates from being included + + if [ "${HERMETIC}" != "true" ] && [ -e /activation-key/org ]; then + cp -r --preserve=mode "$ACTIVATION_KEY_PATH" /tmp/activation-key + mkdir -p /shared/rhsm/etc/pki/entitlement + mkdir -p /shared/rhsm/etc/pki/consumer + + VOLUME_MOUNTS+=(-v /tmp/activation-key:/activation-key \ + -v /shared/rhsm/etc/pki/entitlement:/etc/pki/entitlement:Z \ + -v /shared/rhsm/etc/pki/consumer:/etc/pki/consumer:Z) + echo "Adding activation key to the build" + + if ! grep -E "^[^#]*subscription-manager.[^#]*register" "$dockerfile_path"; then + # user is not running registration in the Containerfile: pre-register. + echo "Pre-registering with subscription manager." + subscription-manager register --org "$(cat /tmp/activation-key/org)" --activationkey "$(cat /tmp/activation-key/activationkey)" + trap 'subscription-manager unregister || true' EXIT + + # copy generated certificates to /shared volume + cp /etc/pki/entitlement/*.pem /shared/rhsm/etc/pki/entitlement + cp /etc/pki/consumer/*.pem /shared/rhsm/etc/pki/consumer + + # and then mount get /etc/rhsm/ca/redhat-uep.pem into /run/secrets/rhsm/ca + VOLUME_MOUNTS+=(--volume /etc/rhsm/ca/redhat-uep.pem:/etc/rhsm/ca/redhat-uep.pem:Z) + fi + + elif [ "${HERMETIC}" != "true" ] && find /entitlement -name "*.pem" >> null; then + cp -r --preserve=mode "$ENTITLEMENT_PATH" /tmp/entitlement + VOLUME_MOUNTS+=(--volume /tmp/entitlement:/etc/pki/entitlement) + echo "Adding the entitlement to the build" + fi + + if [ -n "${ADDITIONAL_VOLUME_MOUNTS-}" ]; then + # ADDITIONAL_VOLUME_MOUNTS allows to specify more volumes for the build. + # Instrumented builds (SAST) use this step as their base and add some other tools. + while read -r volume_mount; do + VOLUME_MOUNTS+=("--volume=$volume_mount") + done <<< "$ADDITIONAL_VOLUME_MOUNTS" + fi + + ADDITIONAL_SECRET_PATH="/additional-secret" + ADDITIONAL_SECRET_TMP="/tmp/additional-secret" + if [ -d "$ADDITIONAL_SECRET_PATH" ]; then + cp -r --preserve=mode -L "$ADDITIONAL_SECRET_PATH" $ADDITIONAL_SECRET_TMP + while read -r filename; do + echo "Adding the secret ${ADDITIONAL_SECRET}/${filename} to the build, available at /run/secrets/${ADDITIONAL_SECRET}/${filename}" + BUILDAH_ARGS+=("--secret=id=${ADDITIONAL_SECRET}/${filename},src=$ADDITIONAL_SECRET_TMP/${filename}") + done < <(find $ADDITIONAL_SECRET_TMP -maxdepth 1 -type f -exec basename {} \;) + fi + + # Prevent ShellCheck from giving a warning because 'image' is defined and 'IMAGE' is not. + declare IMAGE + + buildah_cmd_array=( + buildah build + "${VOLUME_MOUNTS[@]}" + "${BUILDAH_ARGS[@]}" + "${LABELS[@]}" + --tls-verify="$TLSVERIFY" --no-cache + --ulimit nofile=4096:4096 + -f "$dockerfile_copy" -t "$IMAGE" . + ) + buildah_cmd=$(printf "%q " "${buildah_cmd_array[@]}") + + if [ "${HERMETIC}" == "true" ]; then + # enabling loopback adapter enables Bazel builds to work in hermetic mode. + command="ip link set lo up && $buildah_cmd" + else + command="$buildah_cmd" + fi + + # disable host subcription manager integration + find /usr/share/rhel/secrets -type l -exec unlink {} \; + + unshare -Uf "${UNSHARE_ARGS[@]}" --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -w "${SOURCE_CODE_DIR}/$CONTEXT" -- sh -c "$command" + + container=$(buildah from --pull-never "$IMAGE") + + # Save the SBOM produced by Cachi2 so it can be merged into the final SBOM later + if [ -f "/tmp/cachi2/output/bom.json" ]; then + echo "Making copy of sbom-cachi2.json" + cp /tmp/cachi2/output/bom.json ./sbom-cachi2.json + fi + + buildah mount $container | tee /shared/container_path + # delete symlinks - they may point outside the container rootfs, messing with SBOM scanners + find $(cat /shared/container_path) -xtype l -delete + echo $container > /shared/container_name + + touch /shared/base_images_digests + echo "Recording base image digests used" + for image in $BASE_IMAGES; do + base_image_digest=$(buildah images --format '{{ .Name }}:{{ .Tag }}@{{ .Digest }}' --filter reference="$image") + # In some cases, there might be BASE_IMAGES, but not any associated digest. This happens + # if buildah did not use that particular image during build because it was skipped + if [ -n "$base_image_digest" ]; then + echo "$image $base_image_digest" | tee -a /shared/base_images_digests + fi + done + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /entitlement + name: etc-pki-entitlement + - mountPath: /activation-key + name: activation-key + - mountPath: /additional-secret + name: additional-secret + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/icm-injection-scripts:latest@sha256:523689a26c74790be67d6036820abfb9aa10bec6efff98cc1c9b74bd2f99eeb1 + name: icm + script: | + #!/bin/bash + set -euo pipefail + /scripts/inject-icm.sh "$IMAGE" + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: push + script: | + #!/bin/bash + set -e + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + retries=5 + # Push to a unique tag based on the TaskRun name to avoid race conditions + echo "Pushing to ${IMAGE%:*}:${TASKRUN_NAME}" + if ! buildah push \ + --retry "$retries" \ + --tls-verify="$TLSVERIFY" \ + "$IMAGE" \ + "docker://${IMAGE%:*}:$(context.taskRun.name)"; + then + echo "Failed to push sbom image to ${IMAGE%:*}:$(context.taskRun.name) after ${retries} tries" + exit 1 + fi + + # Push to a tag based on the git revision + echo "Pushing to ${IMAGE}" + if ! buildah push \ + --retry "$retries" \ + --tls-verify="$TLSVERIFY" \ + --digestfile "$(workspaces.source.path)/image-digest" "$IMAGE" \ + "docker://$IMAGE"; + then + echo "Failed to push sbom image to $IMAGE after ${retries} tries" + exit 1 + fi + + cat "$(workspaces.source.path)"/image-digest | tee $(results.IMAGE_DIGEST.path) + echo -n "$IMAGE" | tee $(results.IMAGE_URL.path) + { + echo -n "${IMAGE}@" + cat "$(workspaces.source.path)/image-digest" + } > "$(results.IMAGE_REF.path)" + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate + script: | + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + echo "Running syft on the source directory" + syft dir:"$(workspaces.source.path)/$SOURCE_CODE_DIR/$CONTEXT" --output cyclonedx-json@1.5="$(workspaces.source.path)/sbom-source.json" + echo "Running syft on the image filesystem" + syft dir:"$(cat /shared/container_path)" --output cyclonedx-json@1.5="$(workspaces.source.path)/sbom-image.json" + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + workingDir: $(workspaces.source.path)/source + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + name: analyse-dependencies-java-sbom + script: | + if [ -f /var/lib/containers/java ]; then + /opt/jboss/container/java/run/run-java.sh analyse-dependencies path $(cat /shared/container_path) -s $(workspaces.source.path)/sbom-image.json --task-run-name $(context.taskRun.name) --publishers $(results.SBOM_JAVA_COMPONENTS_COUNT.path) + sed -i 's/^/ /' $(results.SBOM_JAVA_COMPONENTS_COUNT.path) # Workaround for SRVKP-2875 + else + touch $(results.JAVA_COMMUNITY_DEPENDENCIES.path) + fi + securityContext: + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/sbom-utility-scripts@sha256:7f4ce55033077f37eda61c67b4289949471c36d23ae0cc66fd4113e615b3ef93 + name: prepare-sboms + script: | + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + echo "Merging contents of sbom-source.json and sbom-image.json into sbom-cyclonedx.json" + python3 /scripts/merge_syft_sboms.py + + if [ -f "sbom-cachi2.json" ]; then + echo "Merging contents of sbom-cachi2.json into sbom-cyclonedx.json" + python3 /scripts/merge_cachi2_sboms.py sbom-cachi2.json sbom-cyclonedx.json > sbom-temp.json + mv sbom-temp.json sbom-cyclonedx.json + fi + + echo "Adding base images data to sbom-cyclonedx.json" + python3 /scripts/base_images_sbom_script.py \ + --sbom=sbom-cyclonedx.json \ + --parsed-dockerfile=/shared/parsed_dockerfile.json \ + --base-images-digests=/shared/base_images_digests + + echo "Adding image reference to sbom" + IMAGE_URL="$(cat "$(results.IMAGE_URL.path)")" + IMAGE_DIGEST="$(cat "$(results.IMAGE_DIGEST.path)")" + + python3 /scripts/add_image_reference.py \ + --image-url "$IMAGE_URL" \ + --image-digest "$IMAGE_DIGEST" \ + --input-file sbom-cyclonedx.json \ + --output-file /tmp/sbom-cyclonedx.tmp.json + + mv /tmp/sbom-cyclonedx.tmp.json sbom-cyclonedx.json + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/appstudio-utils:48c311af02858e2422d6229600e9959e496ddef1@sha256:91ddd999271f65d8ec8487b10f3dd378f81aa894e11b9af4d10639fd52bba7e8 + name: upload-sbom + script: | + #!/bin/bash + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + cosign attach sbom --sbom sbom-cyclonedx.json --type cyclonedx "$(cat "$(results.IMAGE_REF.path)")" + + # Remove tag from IMAGE while allowing registry to contain a port number. + sbom_repo="${IMAGE%:*}" + sbom_digest="$(sha256sum sbom-cyclonedx.json | cut -d' ' -f1)" + # The SBOM_BLOB_URL is created by `cosign attach sbom`. + echo -n "${sbom_repo}@sha256:${sbom_digest}" | tee "$(results.SBOM_BLOB_URL.path)" + volumeMounts: + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + volumes: + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared + - name: etc-pki-entitlement + secret: + optional: true + secretName: $(params.ENTITLEMENT_SECRET) + - name: activation-key + secret: + optional: true + secretName: $(params.ACTIVATION_KEY) + - name: additional-secret + secret: + optional: true + secretName: $(params.ADDITIONAL_SECRET) + - configMap: + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) + optional: true + name: trusted-ca + workspaces: + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah-20gb/0.1/buildah-20gb.yaml b/task/buildah-20gb/0.1/buildah-20gb.yaml new file mode 100644 index 0000000000..bd0046ff85 --- /dev/null +++ b/task/buildah-20gb/0.1/buildah-20gb.yaml @@ -0,0 +1,585 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2025-01-21T00:00:00Z" + build.appstudio.redhat.com/expiry-message: 'Instead of using this task, switch + to buildah task (you can find latest bundle reference here: https://quay.io/repository/konflux-ci/tekton-catalog/task-buildah) + and configure build step resources as described in the doc: https://konflux-ci.dev/docs/how-tos/configuring/overriding-compute-resources/' + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: "0.1" + build.appstudio.redhat.com/build_type: docker + name: buildah-20gb +spec: + description: |- + Buildah task builds source code into a container image and pushes the image into container registry using buildah tool. + In addition it generates a SBOM file, injects the SBOM file into final container image and pushes the SBOM file as separate image using cosign tool. + When [Java dependency rebuild](https://redhat-appstudio.github.io/docs.stonesoup.io/Documentation/main/cli/proc_enabled_java_dependencies.html) is enabled it triggers rebuilds of Java artifacts. + When prefetch-dependencies task was activated it is using its artifacts to run build in hermetic environment. + params: + - description: Reference of the image buildah will produce. + name: IMAGE + type: string + - default: "" + description: Deprecated. Has no effect. Will be removed in the future. + name: BUILDER_IMAGE + type: string + - default: ./Dockerfile + description: Path to the Dockerfile to build. + name: DOCKERFILE + type: string + - default: . + description: Path to the directory to use as context. + name: CONTEXT + type: string + - default: "true" + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) + name: TLSVERIFY + type: string + - default: "" + description: unused, should be removed in next task version + name: DOCKER_AUTH + type: string + - default: "false" + description: Determines if build will be executed without network access. + name: HERMETIC + type: string + - default: "" + description: In case it is not empty, the prefetched content should be made available + to the build. + name: PREFETCH_INPUT + type: string + - default: "" + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. + name: IMAGE_EXPIRES_AFTER + type: string + - default: "" + description: The image is built from this commit. + name: COMMIT_SHA + type: string + - default: repos.d + description: Path in the git repository in which yum repository files are stored + name: YUM_REPOS_D_SRC + - default: fetched.repos.d + description: Path in source workspace where dynamically-fetched repos are present + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE + type: string + - default: etc-pki-entitlement + description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET + type: string + - default: activation-key + description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY + type: string + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS + type: array + - default: "" + description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE + type: string + - default: trusted-ca + description: The name of the ConfigMap to read CA bundle data from. + name: caTrustConfigMapName + type: string + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey + type: string + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES + type: string + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH + type: string + - default: vfs + description: Storage driver to configure for buildah + name: STORAGE_DRIVER + type: string + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES + type: string + results: + - description: Digest of the image just built + name: IMAGE_DIGEST + - description: Image repository and tag where the built image was pushed + name: IMAGE_URL + - description: Digests of the base images used for build + name: BASE_IMAGES_DIGESTS + - description: The counting of Java components by publisher in JSON format + name: SBOM_JAVA_COMPONENTS_COUNT + type: string + - description: The Java dependencies that came from community sources such as Maven + central. + name: JAVA_COMMUNITY_DEPENDENCIES + stepTemplate: + env: + - name: BUILDAH_FORMAT + value: oci + - name: STORAGE_DRIVER + value: $(params.STORAGE_DRIVER) + - name: HERMETIC + value: $(params.HERMETIC) + - name: CONTEXT + value: $(params.CONTEXT) + - name: DOCKERFILE + value: $(params.DOCKERFILE) + - name: IMAGE + value: $(params.IMAGE) + - name: TLSVERIFY + value: $(params.TLSVERIFY) + - name: IMAGE_EXPIRES_AFTER + value: $(params.IMAGE_EXPIRES_AFTER) + - name: YUM_REPOS_D_SRC + value: $(params.YUM_REPOS_D_SRC) + - name: YUM_REPOS_D_FETCHED + value: $(params.YUM_REPOS_D_FETCHED) + - name: YUM_REPOS_D_TARGET + value: $(params.YUM_REPOS_D_TARGET) + - name: TARGET_STAGE + value: $(params.TARGET_STAGE) + - name: PARAM_BUILDER_IMAGE + value: $(params.BUILDER_IMAGE) + - name: ENTITLEMENT_SECRET + value: $(params.ENTITLEMENT_SECRET) + - name: ADDITIONAL_SECRET + value: $(params.ADDITIONAL_SECRET) + - name: BUILD_ARGS_FILE + value: $(params.BUILD_ARGS_FILE) + - name: ADD_CAPABILITIES + value: $(params.ADD_CAPABILITIES) + - name: SQUASH + value: $(params.SQUASH) + - name: SKIP_UNUSED_STAGES + value: $(params.SKIP_UNUSED_STAGES) + volumeMounts: + - mountPath: /shared + name: shared + steps: + - args: + - $(params.BUILD_ARGS[*]) + computeResources: + limits: + memory: 20Gi + requests: + cpu: 250m + memory: 16Gi + env: + - name: COMMIT_SHA + value: $(params.COMMIT_SHA) + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build + script: | + if [ -n "${PARAM_BUILDER_IMAGE}" ]; then + echo "WARNING: provided deprecated BUILDER_IMAGE parameter has no effect." + fi + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + SOURCE_CODE_DIR=source + if [ -e "$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" + elif [ -e "$SOURCE_CODE_DIR/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$DOCKERFILE" + elif echo "$DOCKERFILE" | grep -q "^https\?://"; then + echo "Fetch Dockerfile from $DOCKERFILE" + dockerfile_path=$(mktemp --suffix=-Dockerfile) + http_code=$(curl -s -L -w "%{http_code}" --output "$dockerfile_path" "$DOCKERFILE") + if [ $http_code != 200 ]; then + echo "No Dockerfile is fetched. Server responds $http_code" + exit 1 + fi + http_code=$(curl -s -L -w "%{http_code}" --output "$dockerfile_path.dockerignore.tmp" "$DOCKERFILE.dockerignore") + if [ $http_code = 200 ]; then + echo "Fetched .dockerignore from $DOCKERFILE.dockerignore" + mv "$dockerfile_path.dockerignore.tmp" $SOURCE_CODE_DIR/$CONTEXT/.dockerignore + fi + else + echo "Cannot find Dockerfile $DOCKERFILE" + exit 1 + fi + if [ -n "$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR" ] && grep -q '^\s*RUN \(./\)\?mvn' "$dockerfile_path"; then + sed -i -e "s|^\s*RUN \(\(./\)\?mvn\)\(.*\)|RUN echo \"mirror.defaulthttp://$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR/v1/cache/default/0/*\" > /tmp/settings.yaml; \1 -s /tmp/settings.yaml \3|g" "$dockerfile_path" + touch /var/lib/containers/java + fi + + # Fixing group permission on /var/lib/containers + chown root:root /var/lib/containers + + sed -i 's/^\s*short-name-mode\s*=\s*.*/short-name-mode = "disabled"/' /etc/containers/registries.conf + + # Setting new namespace to run buildah - 2^32-2 + echo 'root:1:4294967294' | tee -a /etc/subuid >> /etc/subgid + + BUILDAH_ARGS=() + + BASE_IMAGES=$(grep -i '^\s*FROM' "$dockerfile_path" | sed 's/--platform=\S*//' | awk '{print $2}' | (grep -v ^oci-archive: || true)) + if [ "${HERMETIC}" == "true" ]; then + BUILDAH_ARGS+=("--pull=never") + UNSHARE_ARGS="--net" + for image in $BASE_IMAGES; do + if [ "${image}" != "scratch" ]; then + unshare -Ufp --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -- buildah pull $image + fi + done + echo "Build will be executed with network isolation" + fi + + if [ -n "${TARGET_STAGE}" ]; then + BUILDAH_ARGS+=("--target=${TARGET_STAGE}") + fi + + if [ -n "${BUILD_ARGS_FILE}" ]; then + BUILDAH_ARGS+=("--build-arg-file=$(pwd)/$SOURCE_CODE_DIR/${BUILD_ARGS_FILE}") + fi + + for build_arg in "$@"; do + BUILDAH_ARGS+=("--build-arg=$build_arg") + done + + if [ -n "${ADD_CAPABILITIES}" ]; then + BUILDAH_ARGS+=("--cap-add=${ADD_CAPABILITIES}") + fi + + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + if [ "${SKIP_UNUSED_STAGES}" != "true" ] ; then + BUILDAH_ARGS+=("--skip-unused-stages=false") + fi + + if [ -f "$(workspaces.source.path)/cachi2/cachi2.env" ]; then + cp -r "$(workspaces.source.path)/cachi2" /tmp/ + chmod -R go+rwX /tmp/cachi2 + VOLUME_MOUNTS="--volume /tmp/cachi2:/cachi2" + # Read in the whole file (https://unix.stackexchange.com/questions/533277), then + # for each RUN ... line insert the cachi2.env command *after* any options like --mount + sed -E -i \ + -e 'H;1h;$!d;x' \ + -e 's@^\s*(run((\s|\\\n)+-\S+)*(\s|\\\n)+)@\1. /cachi2/cachi2.env \&\& \\\n @igM' \ + "$dockerfile_path" + echo "Prefetched content will be made available" + + prefetched_repo_for_my_arch="/tmp/cachi2/output/deps/rpm/$(uname -m)/repos.d/cachi2.repo" + if [ -f "$prefetched_repo_for_my_arch" ]; then + echo "Adding $prefetched_repo_for_my_arch to $YUM_REPOS_D_FETCHED" + mkdir -p "$YUM_REPOS_D_FETCHED" + cp --no-clobber "$prefetched_repo_for_my_arch" "$YUM_REPOS_D_FETCHED" + fi + fi + + # if yum repofiles stored in git, copy them to mount point outside the source dir + if [ -d "${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}" ]; then + mkdir -p ${YUM_REPOS_D_FETCHED} + cp -r ${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}/* ${YUM_REPOS_D_FETCHED} + fi + + # if anything in the repofiles mount point (either fetched or from git), mount it + if [ -d "${YUM_REPOS_D_FETCHED}" ]; then + chmod -R go+rwX ${YUM_REPOS_D_FETCHED} + mount_point=$(realpath ${YUM_REPOS_D_FETCHED}) + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume ${mount_point}:${YUM_REPOS_D_TARGET}" + fi + + LABELS=( + "--label" "build-date=$(date -u +'%Y-%m-%dT%H:%M:%S')" + "--label" "architecture=$(uname -m)" + "--label" "vcs-type=git" + ) + [ -n "$COMMIT_SHA" ] && LABELS+=("--label" "vcs-ref=$COMMIT_SHA") + [ -n "$IMAGE_EXPIRES_AFTER" ] && LABELS+=("--label" "quay.expires-after=$IMAGE_EXPIRES_AFTER") + + ACTIVATION_KEY_PATH="/activation-key" + ENTITLEMENT_PATH="/entitlement" + + if [ -e /activation-key/org ]; then + cp -r --preserve=mode "$ACTIVATION_KEY_PATH" /tmp/activation-key + mkdir /shared/rhsm-tmp + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume /tmp/activation-key:/activation-key -v /shared/rhsm-tmp:/etc/pki/entitlement:Z" + echo "Adding activation key to the build" + + elif find /entitlement -name "*.pem" >> null; then + cp -r --preserve=mode "$ENTITLEMENT_PATH" /tmp/entitlement + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume /tmp/entitlement:/etc/pki/entitlement" + echo "Adding the entitlement to the build" + fi + + ADDITIONAL_SECRET_PATH="/additional-secret" + ADDITIONAL_SECRET_TMP="/tmp/additional-secret" + if [ -d "$ADDITIONAL_SECRET_PATH" ]; then + cp -r --preserve=mode -L "$ADDITIONAL_SECRET_PATH" $ADDITIONAL_SECRET_TMP + while read -r filename; do + echo "Adding the secret ${ADDITIONAL_SECRET}/${filename} to the build, available at /run/secrets/${ADDITIONAL_SECRET}/${filename}" + BUILDAH_ARGS+=("--secret=id=${ADDITIONAL_SECRET}/${filename},src=$ADDITIONAL_SECRET_TMP/${filename}") + done < <(find $ADDITIONAL_SECRET_TMP -maxdepth 1 -type f -exec basename {} \;) + fi + + unshare -Uf $UNSHARE_ARGS --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -w ${SOURCE_CODE_DIR}/$CONTEXT -- buildah build \ + $VOLUME_MOUNTS \ + "${BUILDAH_ARGS[@]}" \ + "${LABELS[@]}" \ + --tls-verify=$TLSVERIFY --no-cache \ + --ulimit nofile=4096:4096 \ + -f "$dockerfile_path" -t $IMAGE . + + container=$(buildah from --pull-never $IMAGE) + buildah mount $container | tee /shared/container_path + # delete symlinks - they may point outside the container rootfs, messing with SBOM scanners + find $(cat /shared/container_path) -xtype l -delete + echo $container > /shared/container_name + + # Save the SBOM produced by Cachi2 so it can be merged into the final SBOM later + if [ -f "/tmp/cachi2/output/bom.json" ]; then + cp /tmp/cachi2/output/bom.json ./sbom-cachi2.json + fi + + # Expose base image digests + touch $(results.BASE_IMAGES_DIGESTS.path) + for image in $BASE_IMAGES; do + if [ "${image}" != "scratch" ]; then + buildah images --format '{{ .Name }}:{{ .Tag }}@{{ .Digest }}' --filter reference="$image" >> $(results.BASE_IMAGES_DIGESTS.path) + fi + done + + # Needed to generate base images SBOM + echo "$BASE_IMAGES" > $(workspaces.source.path)/base_images_from_dockerfile + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /entitlement + name: etc-pki-entitlement + - mountPath: /activation-key + name: activation-key + - mountPath: /additional-secret + name: additional-secret + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate + script: | + echo "Running syft on the source directory" + syft dir:$(workspaces.source.path)/source --output cyclonedx-json=$(workspaces.source.path)/sbom-source.json + echo "Running syft on the image filesystem" + syft dir:$(cat /shared/container_path) --output cyclonedx-json=$(workspaces.source.path)/sbom-image.json + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + workingDir: $(workspaces.source.path)/source + - image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + name: analyse-dependencies-java-sbom + script: | + if [ -f /var/lib/containers/java ]; then + /opt/jboss/container/java/run/run-java.sh analyse-dependencies path $(cat /shared/container_path) -s $(workspaces.source.path)/sbom-image.json --task-run-name $(context.taskRun.name) --publishers $(results.SBOM_JAVA_COMPONENTS_COUNT.path) + sed -i 's/^/ /' $(results.SBOM_JAVA_COMPONENTS_COUNT.path) # Workaround for SRVKP-2875 + else + touch $(results.JAVA_COMMUNITY_DEPENDENCIES.path) + fi + securityContext: + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + name: merge-syft-sboms + script: | + #!/bin/python3 + import json + + # load SBOMs + with open("./sbom-image.json") as f: + image_sbom = json.load(f) + + with open("./sbom-source.json") as f: + source_sbom = json.load(f) + + # fetch unique components from available SBOMs + def get_identifier(component): + return component["name"] + '@' + component.get("version", "") + + image_sbom_components = image_sbom.setdefault("components", []) + existing_components = [get_identifier(component) for component in image_sbom_components] + + source_sbom_components = source_sbom.get("components", []) + for component in source_sbom_components: + if get_identifier(component) not in existing_components: + image_sbom_components.append(component) + existing_components.append(get_identifier(component)) + + image_sbom_components.sort(key=lambda c: get_identifier(c)) + + # write the CycloneDX unified SBOM + with open("./sbom-cyclonedx.json", "w") as f: + json.dump(image_sbom, f, indent=4) + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/cachi2:0.17.0@sha256:963870f04aeb4a207e79b8eacb47e16c8faa7451f52e92959e0e16cdbd258fb3 + name: merge-cachi2-sbom + script: | + if [ -f "sbom-cachi2.json" ]; then + echo "Merging contents of sbom-cachi2.json into sbom-cyclonedx.json" + merge_syft_sbom sbom-cachi2.json sbom-cyclonedx.json > sbom-temp.json + mv sbom-temp.json sbom-cyclonedx.json + else + echo "Skipping step since no Cachi2 SBOM was produced" + fi + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + name: create-purl-sbom + script: | + #!/bin/python3 + import json + + with open("./sbom-cyclonedx.json") as f: + cyclonedx_sbom = json.load(f) + + purls = [{"purl": component["purl"]} for component in cyclonedx_sbom.get("components", []) if "purl" in component] + purl_content = {"image_contents": {"dependencies": purls}} + + with open("sbom-purl.json", "w") as output_file: + json.dump(purl_content, output_file, indent=4) + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - env: + - name: BASE_IMAGES_DIGESTS_PATH + value: $(results.BASE_IMAGES_DIGESTS.path) + image: quay.io/redhat-appstudio/base-images-sbom-script@sha256:667669e3def018f9dbb8eaf8868887a40bc07842221e9a98f6787edcff021840 + name: create-base-images-sbom + script: | + python3 /app/base_images_sbom_script.py --sbom=sbom-cyclonedx.json --base-images-from-dockerfile=base_images_from_dockerfile --base-images-digests=$BASE_IMAGES_DIGESTS_PATH + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - computeResources: {} + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: inject-sbom-and-push + script: | + if [ -n "${PARAM_BUILDER_IMAGE}" ]; then + echo "WARNING: provided deprecated BUILDER_IMAGE parameter has no effect." + fi + + base_image_name=$(buildah inspect --format '{{ index .ImageAnnotations "org.opencontainers.image.base.name"}}' $IMAGE | cut -f1 -d'@') + base_image_digest=$(buildah inspect --format '{{ index .ImageAnnotations "org.opencontainers.image.base.digest"}}' $IMAGE) + container=$(buildah from --pull-never $IMAGE) + buildah copy $container sbom-cyclonedx.json sbom-purl.json /root/buildinfo/content_manifests/ + buildah config -a org.opencontainers.image.base.name=${base_image_name} -a org.opencontainers.image.base.digest=${base_image_digest} $container + + BUILDAH_ARGS=() + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + buildah commit "${BUILDAH_ARGS[@]}" $container $IMAGE + + status=-1 + max_run=5 + sleep_sec=10 + for run in $(seq 1 $max_run); do + status=0 + [ "$run" -gt 1 ] && sleep $sleep_sec + echo "Pushing sbom image to registry" + buildah push \ + --tls-verify=$TLSVERIFY \ + --digestfile $(workspaces.source.path)/image-digest $IMAGE \ + docker://$IMAGE && break || status=$? + done + if [ "$status" -ne 0 ]; then + echo "Failed to push sbom image to registry after ${max_run} tries" + exit 1 + fi + + cat "$(workspaces.source.path)"/image-digest | tee $(results.IMAGE_DIGEST.path) + echo -n "$IMAGE" | tee $(results.IMAGE_URL.path) + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + workingDir: $(workspaces.source.path) + - args: + - attach + - sbom + - --sbom + - sbom-cyclonedx.json + - --type + - cyclonedx + - $(params.IMAGE) + image: quay.io/redhat-appstudio/cosign:v2.1.1@sha256:c883d6f8d39148f2cea71bff4622d196d89df3e510f36c140c097b932f0dd5d5 + name: upload-sbom + workingDir: $(workspaces.source.path) + volumes: + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared + - name: etc-pki-entitlement + secret: + optional: true + secretName: $(params.ENTITLEMENT_SECRET) + - name: activation-key + secret: + optional: true + secretName: $(params.ACTIVATION_KEY) + - name: additional-secret + secret: + optional: true + secretName: $(params.ADDITIONAL_SECRET) + - configMap: + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) + optional: true + name: trusted-ca + workspaces: + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah-20gb/0.2/buildah-20gb.yaml b/task/buildah-20gb/0.2/buildah-20gb.yaml new file mode 100644 index 0000000000..87dd41ee3a --- /dev/null +++ b/task/buildah-20gb/0.2/buildah-20gb.yaml @@ -0,0 +1,742 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2025-01-21T00:00:00Z" + build.appstudio.redhat.com/expiry-message: 'Instead of using this task, switch + to buildah task (you can find latest bundle reference here: https://quay.io/repository/konflux-ci/tekton-catalog/task-buildah) + and configure build step resources as described in the doc: https://konflux-ci.dev/docs/how-tos/configuring/overriding-compute-resources/' + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: 0.2.1 + build.appstudio.redhat.com/build_type: docker + name: buildah-20gb +spec: + description: |- + Buildah task builds source code into a container image and pushes the image into container registry using buildah tool. + In addition it generates a SBOM file, injects the SBOM file into final container image and pushes the SBOM file as separate image using cosign tool. + When [Java dependency rebuild](https://redhat-appstudio.github.io/docs.stonesoup.io/Documentation/main/cli/proc_enabled_java_dependencies.html) is enabled it triggers rebuilds of Java artifacts. + When prefetch-dependencies task was activated it is using its artifacts to run build in hermetic environment. + params: + - description: Reference of the image buildah will produce. + name: IMAGE + type: string + - default: ./Dockerfile + description: Path to the Dockerfile to build. + name: DOCKERFILE + type: string + - default: . + description: Path to the directory to use as context. + name: CONTEXT + type: string + - default: "true" + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) + name: TLSVERIFY + type: string + - default: "false" + description: Determines if build will be executed without network access. + name: HERMETIC + type: string + - default: "" + description: In case it is not empty, the prefetched content should be made available + to the build. + name: PREFETCH_INPUT + type: string + - default: "" + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. + name: IMAGE_EXPIRES_AFTER + type: string + - default: "" + description: The image is built from this commit. + name: COMMIT_SHA + type: string + - default: repos.d + description: Path in the git repository in which yum repository files are stored + name: YUM_REPOS_D_SRC + - default: fetched.repos.d + description: Path in source workspace where dynamically-fetched repos are present + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE + type: string + - default: etc-pki-entitlement + description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET + type: string + - default: activation-key + description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY + type: string + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS + type: array + - default: "" + description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE + type: string + - default: trusted-ca + description: The name of the ConfigMap to read CA bundle data from. + name: caTrustConfigMapName + type: string + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey + type: string + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES + type: string + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH + type: string + - default: vfs + description: Storage driver to configure for buildah + name: STORAGE_DRIVER + type: string + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES + type: string + - default: [] + description: Additional key=value labels that should be applied to the image + name: LABELS + type: array + - default: "false" + description: Whether to enable privileged mode + name: PRIVILEGED_NESTED + type: string + - default: "false" + description: Skip SBOM-related operations. This will likely cause EC policies + to fail if enabled + name: SKIP_SBOM_GENERATION + type: string + results: + - description: Digest of the image just built + name: IMAGE_DIGEST + - description: Image repository and tag where the built image was pushed + name: IMAGE_URL + - description: Image reference of the built image + name: IMAGE_REF + - description: Reference of SBOM blob digest to enable digest-based verification + from provenance + name: SBOM_BLOB_URL + type: string + - description: The counting of Java components by publisher in JSON format + name: SBOM_JAVA_COMPONENTS_COUNT + type: string + - description: The Java dependencies that came from community sources such as Maven + central. + name: JAVA_COMMUNITY_DEPENDENCIES + stepTemplate: + computeResources: + limits: + cpu: "4" + memory: 4Gi + requests: + cpu: "1" + memory: 1Gi + env: + - name: BUILDAH_FORMAT + value: oci + - name: STORAGE_DRIVER + value: $(params.STORAGE_DRIVER) + - name: HERMETIC + value: $(params.HERMETIC) + - name: SOURCE_CODE_DIR + value: source + - name: CONTEXT + value: $(params.CONTEXT) + - name: IMAGE + value: $(params.IMAGE) + - name: TLSVERIFY + value: $(params.TLSVERIFY) + - name: IMAGE_EXPIRES_AFTER + value: $(params.IMAGE_EXPIRES_AFTER) + - name: YUM_REPOS_D_SRC + value: $(params.YUM_REPOS_D_SRC) + - name: YUM_REPOS_D_FETCHED + value: $(params.YUM_REPOS_D_FETCHED) + - name: YUM_REPOS_D_TARGET + value: $(params.YUM_REPOS_D_TARGET) + - name: TARGET_STAGE + value: $(params.TARGET_STAGE) + - name: ENTITLEMENT_SECRET + value: $(params.ENTITLEMENT_SECRET) + - name: ACTIVATION_KEY + value: $(params.ACTIVATION_KEY) + - name: ADDITIONAL_SECRET + value: $(params.ADDITIONAL_SECRET) + - name: BUILD_ARGS_FILE + value: $(params.BUILD_ARGS_FILE) + - name: ADD_CAPABILITIES + value: $(params.ADD_CAPABILITIES) + - name: SQUASH + value: $(params.SQUASH) + - name: SKIP_UNUSED_STAGES + value: $(params.SKIP_UNUSED_STAGES) + - name: PRIVILEGED_NESTED + value: $(params.PRIVILEGED_NESTED) + - name: SKIP_SBOM_GENERATION + value: $(params.SKIP_SBOM_GENERATION) + volumeMounts: + - mountPath: /shared + name: shared + steps: + - args: + - --build-args + - $(params.BUILD_ARGS[*]) + - --labels + - $(params.LABELS[*]) + computeResources: + limits: + cpu: "4" + memory: 20Gi + requests: + cpu: "1" + memory: 16Gi + env: + - name: COMMIT_SHA + value: $(params.COMMIT_SHA) + - name: DOCKERFILE + value: $(params.DOCKERFILE) + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build + script: | + #!/bin/bash + set -euo pipefail + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + if [ -e "$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" + elif [ -e "$SOURCE_CODE_DIR/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$DOCKERFILE" + elif [ -e "$DOCKERFILE" ]; then + # Instrumented builds (SAST) use this custom dockerffile step as their base + dockerfile_path="$DOCKERFILE" + elif echo "$DOCKERFILE" | grep -q "^https\?://"; then + echo "Fetch Dockerfile from $DOCKERFILE" + dockerfile_path=$(mktemp --suffix=-Dockerfile) + http_code=$(curl -s -S -L -w "%{http_code}" --output "$dockerfile_path" "$DOCKERFILE") + if [ $http_code != 200 ]; then + echo "No Dockerfile is fetched. Server responds $http_code" + exit 1 + fi + http_code=$(curl -s -S -L -w "%{http_code}" --output "$dockerfile_path.dockerignore.tmp" "$DOCKERFILE.dockerignore") + if [ $http_code = 200 ]; then + echo "Fetched .dockerignore from $DOCKERFILE.dockerignore" + mv "$dockerfile_path.dockerignore.tmp" $SOURCE_CODE_DIR/$CONTEXT/.dockerignore + fi + else + echo "Cannot find Dockerfile $DOCKERFILE" + exit 1 + fi + + dockerfile_copy=$(mktemp --tmpdir "$(basename "$dockerfile_path").XXXXXX") + cp "$dockerfile_path" "$dockerfile_copy" + + if [ -n "${JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR-}" ] && grep -q '^\s*RUN \(./\)\?mvn' "$dockerfile_copy"; then + sed -i -e "s|^\s*RUN \(\(./\)\?mvn\)\(.*\)|RUN echo \"mirror.defaulthttp://$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR/v1/cache/default/0/*\" > /tmp/settings.yaml; \1 -s /tmp/settings.yaml \3|g" "$dockerfile_copy" + touch /var/lib/containers/java + fi + + # Fixing group permission on /var/lib/containers + chown root:root /var/lib/containers + + sed -i 's/^\s*short-name-mode\s*=\s*.*/short-name-mode = "disabled"/' /etc/containers/registries.conf + + # Setting new namespace to run buildah - 2^32-2 + echo 'root:1:4294967294' | tee -a /etc/subuid >> /etc/subgid + + build_args=() + if [ -n "${BUILD_ARGS_FILE}" ]; then + # Parse BUILD_ARGS_FILE ourselves because dockerfile-json doesn't support it + echo "Parsing ARGs from $BUILD_ARGS_FILE" + mapfile -t build_args < <( + # https://www.mankier.com/1/buildah-build#--build-arg-file + # delete lines that start with # + # delete blank lines + sed -e '/^#/d' -e '/^\s*$/d' "${SOURCE_CODE_DIR}/${BUILD_ARGS_FILE}" + ) + fi + + LABELS=() + # Split `args` into two sets of arguments. + while [[ $# -gt 0 ]]; do + case $1 in + --build-args) + shift + # Note: this may result in multiple --build-arg=KEY=value flags with the same KEY being + # passed to buildah. In that case, the *last* occurrence takes precedence. This is why + # we append BUILD_ARGS after the content of the BUILD_ARGS_FILE + while [[ $# -gt 0 && $1 != --* ]]; do build_args+=("$1"); shift; done + ;; + --labels) + shift + while [[ $# -gt 0 && $1 != --* ]]; do LABELS+=("--label" "$1"); shift; done + ;; + *) + echo "unexpected argument: $1" >&2 + exit 2 + ;; + esac + done + + BUILD_ARG_FLAGS=() + for build_arg in "${build_args[@]}"; do + BUILD_ARG_FLAGS+=("--build-arg=$build_arg") + done + + dockerfile-json "${BUILD_ARG_FLAGS[@]}" "$dockerfile_copy" > /shared/parsed_dockerfile.json + BASE_IMAGES=$( + jq -r '.Stages[] | select(.From | .Stage or .Scratch | not) | .BaseName | select(test("^oci-archive:") | not)' /shared/parsed_dockerfile.json + ) + + BUILDAH_ARGS=() + UNSHARE_ARGS=() + + if [ "${HERMETIC}" == "true" ]; then + BUILDAH_ARGS+=("--pull=never") + UNSHARE_ARGS+=("--net") + + for image in $BASE_IMAGES; do + unshare -Ufp --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -- buildah pull $image + done + echo "Build will be executed with network isolation" + fi + + if [ -n "${TARGET_STAGE}" ]; then + BUILDAH_ARGS+=("--target=${TARGET_STAGE}") + fi + + BUILDAH_ARGS+=("${BUILD_ARG_FLAGS[@]}") + + if [ "${PRIVILEGED_NESTED}" == "true" ]; then + BUILDAH_ARGS+=("--security-opt=label=disable") + BUILDAH_ARGS+=("--cap-add=all") + BUILDAH_ARGS+=("--device=/dev/fuse") + fi + + if [ -n "${ADD_CAPABILITIES}" ]; then + BUILDAH_ARGS+=("--cap-add=${ADD_CAPABILITIES}") + fi + + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + if [ "${SKIP_UNUSED_STAGES}" != "true" ] ; then + BUILDAH_ARGS+=("--skip-unused-stages=false") + fi + + VOLUME_MOUNTS=() + + if [ -f "$(workspaces.source.path)/cachi2/cachi2.env" ]; then + cp -r "$(workspaces.source.path)/cachi2" /tmp/ + chmod -R go+rwX /tmp/cachi2 + VOLUME_MOUNTS+=(--volume /tmp/cachi2:/cachi2) + # Read in the whole file (https://unix.stackexchange.com/questions/533277), then + # for each RUN ... line insert the cachi2.env command *after* any options like --mount + sed -E -i \ + -e 'H;1h;$!d;x' \ + -e 's@^\s*(run((\s|\\\n)+-\S+)*(\s|\\\n)+)@\1. /cachi2/cachi2.env \&\& \\\n @igM' \ + "$dockerfile_copy" + echo "Prefetched content will be made available" + + prefetched_repo_for_my_arch="/tmp/cachi2/output/deps/rpm/$(uname -m)/repos.d/cachi2.repo" + if [ -f "$prefetched_repo_for_my_arch" ]; then + echo "Adding $prefetched_repo_for_my_arch to $YUM_REPOS_D_FETCHED" + mkdir -p "$YUM_REPOS_D_FETCHED" + cp --no-clobber "$prefetched_repo_for_my_arch" "$YUM_REPOS_D_FETCHED" + fi + fi + + # if yum repofiles stored in git, copy them to mount point outside the source dir + if [ -d "${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}" ]; then + mkdir -p ${YUM_REPOS_D_FETCHED} + cp -r ${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}/* ${YUM_REPOS_D_FETCHED} + fi + + # if anything in the repofiles mount point (either fetched or from git), mount it + if [ -d "${YUM_REPOS_D_FETCHED}" ]; then + chmod -R go+rwX ${YUM_REPOS_D_FETCHED} + mount_point=$(realpath ${YUM_REPOS_D_FETCHED}) + VOLUME_MOUNTS+=(--volume "${mount_point}:${YUM_REPOS_D_TARGET}") + fi + + DEFAULT_LABELS=( + "--label" "build-date=$(date -u +'%Y-%m-%dT%H:%M:%S')" + "--label" "architecture=$(uname -m)" + "--label" "vcs-type=git" + ) + [ -n "$COMMIT_SHA" ] && DEFAULT_LABELS+=("--label" "vcs-ref=$COMMIT_SHA") + [ -n "$IMAGE_EXPIRES_AFTER" ] && DEFAULT_LABELS+=("--label" "quay.expires-after=$IMAGE_EXPIRES_AFTER") + + # Concatenate defaults and explicit labels. If a label appears twice, the last one wins. + LABELS=("${DEFAULT_LABELS[@]}" "${LABELS[@]}") + + ACTIVATION_KEY_PATH="/activation-key" + ENTITLEMENT_PATH="/entitlement" + + # 0. if hermetic=true, skip all subscription related stuff + # 1. do not enable activation key and entitlement at same time. If both vars are provided, prefer activation key. + # 2. Activation-keys will be used when the key 'org' exists in the activation key secret. + # 3. try to pre-register and mount files to the correct location so that users do no need to modify Dockerfiles. + # 3. If the Dockerfile contains the string "subcription-manager register", add the activation-keys volume + # to buildah but don't pre-register for backwards compatibility. Mount an empty directory on + # shared emptydir volume to "/etc/pki/entitlement" to prevent certificates from being included + + if [ "${HERMETIC}" != "true" ] && [ -e /activation-key/org ]; then + cp -r --preserve=mode "$ACTIVATION_KEY_PATH" /tmp/activation-key + mkdir -p /shared/rhsm/etc/pki/entitlement + mkdir -p /shared/rhsm/etc/pki/consumer + + VOLUME_MOUNTS+=(-v /tmp/activation-key:/activation-key \ + -v /shared/rhsm/etc/pki/entitlement:/etc/pki/entitlement:Z \ + -v /shared/rhsm/etc/pki/consumer:/etc/pki/consumer:Z) + echo "Adding activation key to the build" + + if ! grep -E "^[^#]*subscription-manager.[^#]*register" "$dockerfile_path"; then + # user is not running registration in the Containerfile: pre-register. + echo "Pre-registering with subscription manager." + subscription-manager register --org "$(cat /tmp/activation-key/org)" --activationkey "$(cat /tmp/activation-key/activationkey)" + trap 'subscription-manager unregister || true' EXIT + + # copy generated certificates to /shared volume + cp /etc/pki/entitlement/*.pem /shared/rhsm/etc/pki/entitlement + cp /etc/pki/consumer/*.pem /shared/rhsm/etc/pki/consumer + + # and then mount get /etc/rhsm/ca/redhat-uep.pem into /run/secrets/rhsm/ca + VOLUME_MOUNTS+=(--volume /etc/rhsm/ca/redhat-uep.pem:/etc/rhsm/ca/redhat-uep.pem:Z) + fi + + elif [ "${HERMETIC}" != "true" ] && find /entitlement -name "*.pem" >> null; then + cp -r --preserve=mode "$ENTITLEMENT_PATH" /tmp/entitlement + VOLUME_MOUNTS+=(--volume /tmp/entitlement:/etc/pki/entitlement) + echo "Adding the entitlement to the build" + fi + + if [ -n "${ADDITIONAL_VOLUME_MOUNTS-}" ]; then + # ADDITIONAL_VOLUME_MOUNTS allows to specify more volumes for the build. + # Instrumented builds (SAST) use this step as their base and add some other tools. + while read -r volume_mount; do + VOLUME_MOUNTS+=("--volume=$volume_mount") + done <<< "$ADDITIONAL_VOLUME_MOUNTS" + fi + + ADDITIONAL_SECRET_PATH="/additional-secret" + ADDITIONAL_SECRET_TMP="/tmp/additional-secret" + if [ -d "$ADDITIONAL_SECRET_PATH" ]; then + cp -r --preserve=mode -L "$ADDITIONAL_SECRET_PATH" $ADDITIONAL_SECRET_TMP + while read -r filename; do + echo "Adding the secret ${ADDITIONAL_SECRET}/${filename} to the build, available at /run/secrets/${ADDITIONAL_SECRET}/${filename}" + BUILDAH_ARGS+=("--secret=id=${ADDITIONAL_SECRET}/${filename},src=$ADDITIONAL_SECRET_TMP/${filename}") + done < <(find $ADDITIONAL_SECRET_TMP -maxdepth 1 -type f -exec basename {} \;) + fi + + # Prevent ShellCheck from giving a warning because 'image' is defined and 'IMAGE' is not. + declare IMAGE + + buildah_cmd_array=( + buildah build + "${VOLUME_MOUNTS[@]}" + "${BUILDAH_ARGS[@]}" + "${LABELS[@]}" + --tls-verify="$TLSVERIFY" --no-cache + --ulimit nofile=4096:4096 + -f "$dockerfile_copy" -t "$IMAGE" . + ) + buildah_cmd=$(printf "%q " "${buildah_cmd_array[@]}") + + if [ "${HERMETIC}" == "true" ]; then + # enabling loopback adapter enables Bazel builds to work in hermetic mode. + command="ip link set lo up && $buildah_cmd" + else + command="$buildah_cmd" + fi + + # disable host subcription manager integration + find /usr/share/rhel/secrets -type l -exec unlink {} \; + + unshare -Uf "${UNSHARE_ARGS[@]}" --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -w "${SOURCE_CODE_DIR}/$CONTEXT" -- sh -c "$command" + + container=$(buildah from --pull-never "$IMAGE") + + # Save the SBOM produced by Cachi2 so it can be merged into the final SBOM later + if [ -f "/tmp/cachi2/output/bom.json" ]; then + echo "Making copy of sbom-cachi2.json" + cp /tmp/cachi2/output/bom.json ./sbom-cachi2.json + fi + + buildah mount $container | tee /shared/container_path + # delete symlinks - they may point outside the container rootfs, messing with SBOM scanners + find $(cat /shared/container_path) -xtype l -delete + echo $container > /shared/container_name + + touch /shared/base_images_digests + echo "Recording base image digests used" + for image in $BASE_IMAGES; do + base_image_digest=$(buildah images --format '{{ .Name }}:{{ .Tag }}@{{ .Digest }}' --filter reference="$image") + # In some cases, there might be BASE_IMAGES, but not any associated digest. This happens + # if buildah did not use that particular image during build because it was skipped + if [ -n "$base_image_digest" ]; then + echo "$image $base_image_digest" | tee -a /shared/base_images_digests + fi + done + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /entitlement + name: etc-pki-entitlement + - mountPath: /activation-key + name: activation-key + - mountPath: /additional-secret + name: additional-secret + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/icm-injection-scripts:latest@sha256:523689a26c74790be67d6036820abfb9aa10bec6efff98cc1c9b74bd2f99eeb1 + name: icm + script: | + #!/bin/bash + set -euo pipefail + /scripts/inject-icm.sh "$IMAGE" + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: push + script: | + #!/bin/bash + set -e + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + retries=5 + # Push to a unique tag based on the TaskRun name to avoid race conditions + echo "Pushing to ${IMAGE%:*}:${TASKRUN_NAME}" + if ! buildah push \ + --retry "$retries" \ + --tls-verify="$TLSVERIFY" \ + "$IMAGE" \ + "docker://${IMAGE%:*}:$(context.taskRun.name)"; + then + echo "Failed to push sbom image to ${IMAGE%:*}:$(context.taskRun.name) after ${retries} tries" + exit 1 + fi + + # Push to a tag based on the git revision + echo "Pushing to ${IMAGE}" + if ! buildah push \ + --retry "$retries" \ + --tls-verify="$TLSVERIFY" \ + --digestfile "$(workspaces.source.path)/image-digest" "$IMAGE" \ + "docker://$IMAGE"; + then + echo "Failed to push sbom image to $IMAGE after ${retries} tries" + exit 1 + fi + + cat "$(workspaces.source.path)"/image-digest | tee $(results.IMAGE_DIGEST.path) + echo -n "$IMAGE" | tee $(results.IMAGE_URL.path) + { + echo -n "${IMAGE}@" + cat "$(workspaces.source.path)/image-digest" + } > "$(results.IMAGE_REF.path)" + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate + script: | + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + echo "Running syft on the source directory" + syft dir:"$(workspaces.source.path)/$SOURCE_CODE_DIR/$CONTEXT" --output cyclonedx-json@1.5="$(workspaces.source.path)/sbom-source.json" + echo "Running syft on the image filesystem" + syft dir:"$(cat /shared/container_path)" --output cyclonedx-json@1.5="$(workspaces.source.path)/sbom-image.json" + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + workingDir: $(workspaces.source.path)/source + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + name: analyse-dependencies-java-sbom + script: | + if [ -f /var/lib/containers/java ]; then + /opt/jboss/container/java/run/run-java.sh analyse-dependencies path $(cat /shared/container_path) -s $(workspaces.source.path)/sbom-image.json --task-run-name $(context.taskRun.name) --publishers $(results.SBOM_JAVA_COMPONENTS_COUNT.path) + sed -i 's/^/ /' $(results.SBOM_JAVA_COMPONENTS_COUNT.path) # Workaround for SRVKP-2875 + else + touch $(results.JAVA_COMMUNITY_DEPENDENCIES.path) + fi + securityContext: + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/sbom-utility-scripts@sha256:7f4ce55033077f37eda61c67b4289949471c36d23ae0cc66fd4113e615b3ef93 + name: prepare-sboms + script: | + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + echo "Merging contents of sbom-source.json and sbom-image.json into sbom-cyclonedx.json" + python3 /scripts/merge_syft_sboms.py + + if [ -f "sbom-cachi2.json" ]; then + echo "Merging contents of sbom-cachi2.json into sbom-cyclonedx.json" + python3 /scripts/merge_cachi2_sboms.py sbom-cachi2.json sbom-cyclonedx.json > sbom-temp.json + mv sbom-temp.json sbom-cyclonedx.json + fi + + echo "Adding base images data to sbom-cyclonedx.json" + python3 /scripts/base_images_sbom_script.py \ + --sbom=sbom-cyclonedx.json \ + --parsed-dockerfile=/shared/parsed_dockerfile.json \ + --base-images-digests=/shared/base_images_digests + + echo "Adding image reference to sbom" + IMAGE_URL="$(cat "$(results.IMAGE_URL.path)")" + IMAGE_DIGEST="$(cat "$(results.IMAGE_DIGEST.path)")" + + python3 /scripts/add_image_reference.py \ + --image-url "$IMAGE_URL" \ + --image-digest "$IMAGE_DIGEST" \ + --input-file sbom-cyclonedx.json \ + --output-file /tmp/sbom-cyclonedx.tmp.json + + mv /tmp/sbom-cyclonedx.tmp.json sbom-cyclonedx.json + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/appstudio-utils:48c311af02858e2422d6229600e9959e496ddef1@sha256:91ddd999271f65d8ec8487b10f3dd378f81aa894e11b9af4d10639fd52bba7e8 + name: upload-sbom + script: | + #!/bin/bash + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + cosign attach sbom --sbom sbom-cyclonedx.json --type cyclonedx "$(cat "$(results.IMAGE_REF.path)")" + + # Remove tag from IMAGE while allowing registry to contain a port number. + sbom_repo="${IMAGE%:*}" + sbom_digest="$(sha256sum sbom-cyclonedx.json | cut -d' ' -f1)" + # The SBOM_BLOB_URL is created by `cosign attach sbom`. + echo -n "${sbom_repo}@sha256:${sbom_digest}" | tee "$(results.SBOM_BLOB_URL.path)" + volumeMounts: + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + volumes: + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared + - name: etc-pki-entitlement + secret: + optional: true + secretName: $(params.ENTITLEMENT_SECRET) + - name: activation-key + secret: + optional: true + secretName: $(params.ACTIVATION_KEY) + - name: additional-secret + secret: + optional: true + secretName: $(params.ADDITIONAL_SECRET) + - configMap: + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) + optional: true + name: trusted-ca + workspaces: + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah-24gb/0.1/buildah-24gb.yaml b/task/buildah-24gb/0.1/buildah-24gb.yaml new file mode 100644 index 0000000000..27889a9826 --- /dev/null +++ b/task/buildah-24gb/0.1/buildah-24gb.yaml @@ -0,0 +1,586 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2025-01-21T00:00:00Z" + build.appstudio.redhat.com/expiry-message: 'Instead of using this task, switch + to buildah task (you can find latest bundle reference here: https://quay.io/repository/konflux-ci/tekton-catalog/task-buildah) + and configure build step resources as described in the doc: https://konflux-ci.dev/docs/how-tos/configuring/overriding-compute-resources/' + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: "0.1" + build.appstudio.redhat.com/build_type: docker + name: buildah-24gb +spec: + description: |- + Buildah task builds source code into a container image and pushes the image into container registry using buildah tool. + In addition it generates a SBOM file, injects the SBOM file into final container image and pushes the SBOM file as separate image using cosign tool. + When [Java dependency rebuild](https://redhat-appstudio.github.io/docs.stonesoup.io/Documentation/main/cli/proc_enabled_java_dependencies.html) is enabled it triggers rebuilds of Java artifacts. + When prefetch-dependencies task was activated it is using its artifacts to run build in hermetic environment. + params: + - description: Reference of the image buildah will produce. + name: IMAGE + type: string + - default: "" + description: Deprecated. Has no effect. Will be removed in the future. + name: BUILDER_IMAGE + type: string + - default: ./Dockerfile + description: Path to the Dockerfile to build. + name: DOCKERFILE + type: string + - default: . + description: Path to the directory to use as context. + name: CONTEXT + type: string + - default: "true" + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) + name: TLSVERIFY + type: string + - default: "" + description: unused, should be removed in next task version + name: DOCKER_AUTH + type: string + - default: "false" + description: Determines if build will be executed without network access. + name: HERMETIC + type: string + - default: "" + description: In case it is not empty, the prefetched content should be made available + to the build. + name: PREFETCH_INPUT + type: string + - default: "" + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. + name: IMAGE_EXPIRES_AFTER + type: string + - default: "" + description: The image is built from this commit. + name: COMMIT_SHA + type: string + - default: repos.d + description: Path in the git repository in which yum repository files are stored + name: YUM_REPOS_D_SRC + - default: fetched.repos.d + description: Path in source workspace where dynamically-fetched repos are present + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE + type: string + - default: etc-pki-entitlement + description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET + type: string + - default: activation-key + description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY + type: string + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS + type: array + - default: "" + description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE + type: string + - default: trusted-ca + description: The name of the ConfigMap to read CA bundle data from. + name: caTrustConfigMapName + type: string + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey + type: string + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES + type: string + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH + type: string + - default: vfs + description: Storage driver to configure for buildah + name: STORAGE_DRIVER + type: string + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES + type: string + results: + - description: Digest of the image just built + name: IMAGE_DIGEST + - description: Image repository and tag where the built image was pushed + name: IMAGE_URL + - description: Digests of the base images used for build + name: BASE_IMAGES_DIGESTS + - description: The counting of Java components by publisher in JSON format + name: SBOM_JAVA_COMPONENTS_COUNT + type: string + - description: The Java dependencies that came from community sources such as Maven + central. + name: JAVA_COMMUNITY_DEPENDENCIES + stepTemplate: + env: + - name: BUILDAH_FORMAT + value: oci + - name: STORAGE_DRIVER + value: $(params.STORAGE_DRIVER) + - name: HERMETIC + value: $(params.HERMETIC) + - name: CONTEXT + value: $(params.CONTEXT) + - name: DOCKERFILE + value: $(params.DOCKERFILE) + - name: IMAGE + value: $(params.IMAGE) + - name: TLSVERIFY + value: $(params.TLSVERIFY) + - name: IMAGE_EXPIRES_AFTER + value: $(params.IMAGE_EXPIRES_AFTER) + - name: YUM_REPOS_D_SRC + value: $(params.YUM_REPOS_D_SRC) + - name: YUM_REPOS_D_FETCHED + value: $(params.YUM_REPOS_D_FETCHED) + - name: YUM_REPOS_D_TARGET + value: $(params.YUM_REPOS_D_TARGET) + - name: TARGET_STAGE + value: $(params.TARGET_STAGE) + - name: PARAM_BUILDER_IMAGE + value: $(params.BUILDER_IMAGE) + - name: ENTITLEMENT_SECRET + value: $(params.ENTITLEMENT_SECRET) + - name: ADDITIONAL_SECRET + value: $(params.ADDITIONAL_SECRET) + - name: BUILD_ARGS_FILE + value: $(params.BUILD_ARGS_FILE) + - name: ADD_CAPABILITIES + value: $(params.ADD_CAPABILITIES) + - name: SQUASH + value: $(params.SQUASH) + - name: SKIP_UNUSED_STAGES + value: $(params.SKIP_UNUSED_STAGES) + volumeMounts: + - mountPath: /shared + name: shared + steps: + - args: + - $(params.BUILD_ARGS[*]) + computeResources: + limits: + cpu: "20" + memory: 24Gi + requests: + cpu: "10" + memory: 20Gi + env: + - name: COMMIT_SHA + value: $(params.COMMIT_SHA) + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build + script: | + if [ -n "${PARAM_BUILDER_IMAGE}" ]; then + echo "WARNING: provided deprecated BUILDER_IMAGE parameter has no effect." + fi + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + SOURCE_CODE_DIR=source + if [ -e "$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" + elif [ -e "$SOURCE_CODE_DIR/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$DOCKERFILE" + elif echo "$DOCKERFILE" | grep -q "^https\?://"; then + echo "Fetch Dockerfile from $DOCKERFILE" + dockerfile_path=$(mktemp --suffix=-Dockerfile) + http_code=$(curl -s -L -w "%{http_code}" --output "$dockerfile_path" "$DOCKERFILE") + if [ $http_code != 200 ]; then + echo "No Dockerfile is fetched. Server responds $http_code" + exit 1 + fi + http_code=$(curl -s -L -w "%{http_code}" --output "$dockerfile_path.dockerignore.tmp" "$DOCKERFILE.dockerignore") + if [ $http_code = 200 ]; then + echo "Fetched .dockerignore from $DOCKERFILE.dockerignore" + mv "$dockerfile_path.dockerignore.tmp" $SOURCE_CODE_DIR/$CONTEXT/.dockerignore + fi + else + echo "Cannot find Dockerfile $DOCKERFILE" + exit 1 + fi + if [ -n "$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR" ] && grep -q '^\s*RUN \(./\)\?mvn' "$dockerfile_path"; then + sed -i -e "s|^\s*RUN \(\(./\)\?mvn\)\(.*\)|RUN echo \"mirror.defaulthttp://$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR/v1/cache/default/0/*\" > /tmp/settings.yaml; \1 -s /tmp/settings.yaml \3|g" "$dockerfile_path" + touch /var/lib/containers/java + fi + + # Fixing group permission on /var/lib/containers + chown root:root /var/lib/containers + + sed -i 's/^\s*short-name-mode\s*=\s*.*/short-name-mode = "disabled"/' /etc/containers/registries.conf + + # Setting new namespace to run buildah - 2^32-2 + echo 'root:1:4294967294' | tee -a /etc/subuid >> /etc/subgid + + BUILDAH_ARGS=() + + BASE_IMAGES=$(grep -i '^\s*FROM' "$dockerfile_path" | sed 's/--platform=\S*//' | awk '{print $2}' | (grep -v ^oci-archive: || true)) + if [ "${HERMETIC}" == "true" ]; then + BUILDAH_ARGS+=("--pull=never") + UNSHARE_ARGS="--net" + for image in $BASE_IMAGES; do + if [ "${image}" != "scratch" ]; then + unshare -Ufp --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -- buildah pull $image + fi + done + echo "Build will be executed with network isolation" + fi + + if [ -n "${TARGET_STAGE}" ]; then + BUILDAH_ARGS+=("--target=${TARGET_STAGE}") + fi + + if [ -n "${BUILD_ARGS_FILE}" ]; then + BUILDAH_ARGS+=("--build-arg-file=$(pwd)/$SOURCE_CODE_DIR/${BUILD_ARGS_FILE}") + fi + + for build_arg in "$@"; do + BUILDAH_ARGS+=("--build-arg=$build_arg") + done + + if [ -n "${ADD_CAPABILITIES}" ]; then + BUILDAH_ARGS+=("--cap-add=${ADD_CAPABILITIES}") + fi + + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + if [ "${SKIP_UNUSED_STAGES}" != "true" ] ; then + BUILDAH_ARGS+=("--skip-unused-stages=false") + fi + + if [ -f "$(workspaces.source.path)/cachi2/cachi2.env" ]; then + cp -r "$(workspaces.source.path)/cachi2" /tmp/ + chmod -R go+rwX /tmp/cachi2 + VOLUME_MOUNTS="--volume /tmp/cachi2:/cachi2" + # Read in the whole file (https://unix.stackexchange.com/questions/533277), then + # for each RUN ... line insert the cachi2.env command *after* any options like --mount + sed -E -i \ + -e 'H;1h;$!d;x' \ + -e 's@^\s*(run((\s|\\\n)+-\S+)*(\s|\\\n)+)@\1. /cachi2/cachi2.env \&\& \\\n @igM' \ + "$dockerfile_path" + echo "Prefetched content will be made available" + + prefetched_repo_for_my_arch="/tmp/cachi2/output/deps/rpm/$(uname -m)/repos.d/cachi2.repo" + if [ -f "$prefetched_repo_for_my_arch" ]; then + echo "Adding $prefetched_repo_for_my_arch to $YUM_REPOS_D_FETCHED" + mkdir -p "$YUM_REPOS_D_FETCHED" + cp --no-clobber "$prefetched_repo_for_my_arch" "$YUM_REPOS_D_FETCHED" + fi + fi + + # if yum repofiles stored in git, copy them to mount point outside the source dir + if [ -d "${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}" ]; then + mkdir -p ${YUM_REPOS_D_FETCHED} + cp -r ${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}/* ${YUM_REPOS_D_FETCHED} + fi + + # if anything in the repofiles mount point (either fetched or from git), mount it + if [ -d "${YUM_REPOS_D_FETCHED}" ]; then + chmod -R go+rwX ${YUM_REPOS_D_FETCHED} + mount_point=$(realpath ${YUM_REPOS_D_FETCHED}) + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume ${mount_point}:${YUM_REPOS_D_TARGET}" + fi + + LABELS=( + "--label" "build-date=$(date -u +'%Y-%m-%dT%H:%M:%S')" + "--label" "architecture=$(uname -m)" + "--label" "vcs-type=git" + ) + [ -n "$COMMIT_SHA" ] && LABELS+=("--label" "vcs-ref=$COMMIT_SHA") + [ -n "$IMAGE_EXPIRES_AFTER" ] && LABELS+=("--label" "quay.expires-after=$IMAGE_EXPIRES_AFTER") + + ACTIVATION_KEY_PATH="/activation-key" + ENTITLEMENT_PATH="/entitlement" + + if [ -e /activation-key/org ]; then + cp -r --preserve=mode "$ACTIVATION_KEY_PATH" /tmp/activation-key + mkdir /shared/rhsm-tmp + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume /tmp/activation-key:/activation-key -v /shared/rhsm-tmp:/etc/pki/entitlement:Z" + echo "Adding activation key to the build" + + elif find /entitlement -name "*.pem" >> null; then + cp -r --preserve=mode "$ENTITLEMENT_PATH" /tmp/entitlement + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume /tmp/entitlement:/etc/pki/entitlement" + echo "Adding the entitlement to the build" + fi + + ADDITIONAL_SECRET_PATH="/additional-secret" + ADDITIONAL_SECRET_TMP="/tmp/additional-secret" + if [ -d "$ADDITIONAL_SECRET_PATH" ]; then + cp -r --preserve=mode -L "$ADDITIONAL_SECRET_PATH" $ADDITIONAL_SECRET_TMP + while read -r filename; do + echo "Adding the secret ${ADDITIONAL_SECRET}/${filename} to the build, available at /run/secrets/${ADDITIONAL_SECRET}/${filename}" + BUILDAH_ARGS+=("--secret=id=${ADDITIONAL_SECRET}/${filename},src=$ADDITIONAL_SECRET_TMP/${filename}") + done < <(find $ADDITIONAL_SECRET_TMP -maxdepth 1 -type f -exec basename {} \;) + fi + + unshare -Uf $UNSHARE_ARGS --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -w ${SOURCE_CODE_DIR}/$CONTEXT -- buildah build \ + $VOLUME_MOUNTS \ + "${BUILDAH_ARGS[@]}" \ + "${LABELS[@]}" \ + --tls-verify=$TLSVERIFY --no-cache \ + --ulimit nofile=4096:4096 \ + -f "$dockerfile_path" -t $IMAGE . + + container=$(buildah from --pull-never $IMAGE) + buildah mount $container | tee /shared/container_path + # delete symlinks - they may point outside the container rootfs, messing with SBOM scanners + find $(cat /shared/container_path) -xtype l -delete + echo $container > /shared/container_name + + # Save the SBOM produced by Cachi2 so it can be merged into the final SBOM later + if [ -f "/tmp/cachi2/output/bom.json" ]; then + cp /tmp/cachi2/output/bom.json ./sbom-cachi2.json + fi + + # Expose base image digests + touch $(results.BASE_IMAGES_DIGESTS.path) + for image in $BASE_IMAGES; do + if [ "${image}" != "scratch" ]; then + buildah images --format '{{ .Name }}:{{ .Tag }}@{{ .Digest }}' --filter reference="$image" >> $(results.BASE_IMAGES_DIGESTS.path) + fi + done + + # Needed to generate base images SBOM + echo "$BASE_IMAGES" > $(workspaces.source.path)/base_images_from_dockerfile + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /entitlement + name: etc-pki-entitlement + - mountPath: /activation-key + name: activation-key + - mountPath: /additional-secret + name: additional-secret + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate + script: | + echo "Running syft on the source directory" + syft dir:$(workspaces.source.path)/source --output cyclonedx-json=$(workspaces.source.path)/sbom-source.json + echo "Running syft on the image filesystem" + syft dir:$(cat /shared/container_path) --output cyclonedx-json=$(workspaces.source.path)/sbom-image.json + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + workingDir: $(workspaces.source.path)/source + - image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + name: analyse-dependencies-java-sbom + script: | + if [ -f /var/lib/containers/java ]; then + /opt/jboss/container/java/run/run-java.sh analyse-dependencies path $(cat /shared/container_path) -s $(workspaces.source.path)/sbom-image.json --task-run-name $(context.taskRun.name) --publishers $(results.SBOM_JAVA_COMPONENTS_COUNT.path) + sed -i 's/^/ /' $(results.SBOM_JAVA_COMPONENTS_COUNT.path) # Workaround for SRVKP-2875 + else + touch $(results.JAVA_COMMUNITY_DEPENDENCIES.path) + fi + securityContext: + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + name: merge-syft-sboms + script: | + #!/bin/python3 + import json + + # load SBOMs + with open("./sbom-image.json") as f: + image_sbom = json.load(f) + + with open("./sbom-source.json") as f: + source_sbom = json.load(f) + + # fetch unique components from available SBOMs + def get_identifier(component): + return component["name"] + '@' + component.get("version", "") + + image_sbom_components = image_sbom.setdefault("components", []) + existing_components = [get_identifier(component) for component in image_sbom_components] + + source_sbom_components = source_sbom.get("components", []) + for component in source_sbom_components: + if get_identifier(component) not in existing_components: + image_sbom_components.append(component) + existing_components.append(get_identifier(component)) + + image_sbom_components.sort(key=lambda c: get_identifier(c)) + + # write the CycloneDX unified SBOM + with open("./sbom-cyclonedx.json", "w") as f: + json.dump(image_sbom, f, indent=4) + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/cachi2:0.17.0@sha256:963870f04aeb4a207e79b8eacb47e16c8faa7451f52e92959e0e16cdbd258fb3 + name: merge-cachi2-sbom + script: | + if [ -f "sbom-cachi2.json" ]; then + echo "Merging contents of sbom-cachi2.json into sbom-cyclonedx.json" + merge_syft_sbom sbom-cachi2.json sbom-cyclonedx.json > sbom-temp.json + mv sbom-temp.json sbom-cyclonedx.json + else + echo "Skipping step since no Cachi2 SBOM was produced" + fi + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + name: create-purl-sbom + script: | + #!/bin/python3 + import json + + with open("./sbom-cyclonedx.json") as f: + cyclonedx_sbom = json.load(f) + + purls = [{"purl": component["purl"]} for component in cyclonedx_sbom.get("components", []) if "purl" in component] + purl_content = {"image_contents": {"dependencies": purls}} + + with open("sbom-purl.json", "w") as output_file: + json.dump(purl_content, output_file, indent=4) + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - env: + - name: BASE_IMAGES_DIGESTS_PATH + value: $(results.BASE_IMAGES_DIGESTS.path) + image: quay.io/redhat-appstudio/base-images-sbom-script@sha256:667669e3def018f9dbb8eaf8868887a40bc07842221e9a98f6787edcff021840 + name: create-base-images-sbom + script: | + python3 /app/base_images_sbom_script.py --sbom=sbom-cyclonedx.json --base-images-from-dockerfile=base_images_from_dockerfile --base-images-digests=$BASE_IMAGES_DIGESTS_PATH + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - computeResources: {} + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: inject-sbom-and-push + script: | + if [ -n "${PARAM_BUILDER_IMAGE}" ]; then + echo "WARNING: provided deprecated BUILDER_IMAGE parameter has no effect." + fi + + base_image_name=$(buildah inspect --format '{{ index .ImageAnnotations "org.opencontainers.image.base.name"}}' $IMAGE | cut -f1 -d'@') + base_image_digest=$(buildah inspect --format '{{ index .ImageAnnotations "org.opencontainers.image.base.digest"}}' $IMAGE) + container=$(buildah from --pull-never $IMAGE) + buildah copy $container sbom-cyclonedx.json sbom-purl.json /root/buildinfo/content_manifests/ + buildah config -a org.opencontainers.image.base.name=${base_image_name} -a org.opencontainers.image.base.digest=${base_image_digest} $container + + BUILDAH_ARGS=() + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + buildah commit "${BUILDAH_ARGS[@]}" $container $IMAGE + + status=-1 + max_run=5 + sleep_sec=10 + for run in $(seq 1 $max_run); do + status=0 + [ "$run" -gt 1 ] && sleep $sleep_sec + echo "Pushing sbom image to registry" + buildah push \ + --tls-verify=$TLSVERIFY \ + --digestfile $(workspaces.source.path)/image-digest $IMAGE \ + docker://$IMAGE && break || status=$? + done + if [ "$status" -ne 0 ]; then + echo "Failed to push sbom image to registry after ${max_run} tries" + exit 1 + fi + + cat "$(workspaces.source.path)"/image-digest | tee $(results.IMAGE_DIGEST.path) + echo -n "$IMAGE" | tee $(results.IMAGE_URL.path) + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + workingDir: $(workspaces.source.path) + - args: + - attach + - sbom + - --sbom + - sbom-cyclonedx.json + - --type + - cyclonedx + - $(params.IMAGE) + image: quay.io/redhat-appstudio/cosign:v2.1.1@sha256:c883d6f8d39148f2cea71bff4622d196d89df3e510f36c140c097b932f0dd5d5 + name: upload-sbom + workingDir: $(workspaces.source.path) + volumes: + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared + - name: etc-pki-entitlement + secret: + optional: true + secretName: $(params.ENTITLEMENT_SECRET) + - name: activation-key + secret: + optional: true + secretName: $(params.ACTIVATION_KEY) + - name: additional-secret + secret: + optional: true + secretName: $(params.ADDITIONAL_SECRET) + - configMap: + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) + optional: true + name: trusted-ca + workspaces: + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah-24gb/0.2/buildah-24gb.yaml b/task/buildah-24gb/0.2/buildah-24gb.yaml new file mode 100644 index 0000000000..07de6d236b --- /dev/null +++ b/task/buildah-24gb/0.2/buildah-24gb.yaml @@ -0,0 +1,742 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2025-01-21T00:00:00Z" + build.appstudio.redhat.com/expiry-message: 'Instead of using this task, switch + to buildah task (you can find latest bundle reference here: https://quay.io/repository/konflux-ci/tekton-catalog/task-buildah) + and configure build step resources as described in the doc: https://konflux-ci.dev/docs/how-tos/configuring/overriding-compute-resources/' + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: 0.2.1 + build.appstudio.redhat.com/build_type: docker + name: buildah-24gb +spec: + description: |- + Buildah task builds source code into a container image and pushes the image into container registry using buildah tool. + In addition it generates a SBOM file, injects the SBOM file into final container image and pushes the SBOM file as separate image using cosign tool. + When [Java dependency rebuild](https://redhat-appstudio.github.io/docs.stonesoup.io/Documentation/main/cli/proc_enabled_java_dependencies.html) is enabled it triggers rebuilds of Java artifacts. + When prefetch-dependencies task was activated it is using its artifacts to run build in hermetic environment. + params: + - description: Reference of the image buildah will produce. + name: IMAGE + type: string + - default: ./Dockerfile + description: Path to the Dockerfile to build. + name: DOCKERFILE + type: string + - default: . + description: Path to the directory to use as context. + name: CONTEXT + type: string + - default: "true" + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) + name: TLSVERIFY + type: string + - default: "false" + description: Determines if build will be executed without network access. + name: HERMETIC + type: string + - default: "" + description: In case it is not empty, the prefetched content should be made available + to the build. + name: PREFETCH_INPUT + type: string + - default: "" + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. + name: IMAGE_EXPIRES_AFTER + type: string + - default: "" + description: The image is built from this commit. + name: COMMIT_SHA + type: string + - default: repos.d + description: Path in the git repository in which yum repository files are stored + name: YUM_REPOS_D_SRC + - default: fetched.repos.d + description: Path in source workspace where dynamically-fetched repos are present + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE + type: string + - default: etc-pki-entitlement + description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET + type: string + - default: activation-key + description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY + type: string + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS + type: array + - default: "" + description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE + type: string + - default: trusted-ca + description: The name of the ConfigMap to read CA bundle data from. + name: caTrustConfigMapName + type: string + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey + type: string + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES + type: string + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH + type: string + - default: vfs + description: Storage driver to configure for buildah + name: STORAGE_DRIVER + type: string + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES + type: string + - default: [] + description: Additional key=value labels that should be applied to the image + name: LABELS + type: array + - default: "false" + description: Whether to enable privileged mode + name: PRIVILEGED_NESTED + type: string + - default: "false" + description: Skip SBOM-related operations. This will likely cause EC policies + to fail if enabled + name: SKIP_SBOM_GENERATION + type: string + results: + - description: Digest of the image just built + name: IMAGE_DIGEST + - description: Image repository and tag where the built image was pushed + name: IMAGE_URL + - description: Image reference of the built image + name: IMAGE_REF + - description: Reference of SBOM blob digest to enable digest-based verification + from provenance + name: SBOM_BLOB_URL + type: string + - description: The counting of Java components by publisher in JSON format + name: SBOM_JAVA_COMPONENTS_COUNT + type: string + - description: The Java dependencies that came from community sources such as Maven + central. + name: JAVA_COMMUNITY_DEPENDENCIES + stepTemplate: + computeResources: + limits: + cpu: "4" + memory: 4Gi + requests: + cpu: "1" + memory: 1Gi + env: + - name: BUILDAH_FORMAT + value: oci + - name: STORAGE_DRIVER + value: $(params.STORAGE_DRIVER) + - name: HERMETIC + value: $(params.HERMETIC) + - name: SOURCE_CODE_DIR + value: source + - name: CONTEXT + value: $(params.CONTEXT) + - name: IMAGE + value: $(params.IMAGE) + - name: TLSVERIFY + value: $(params.TLSVERIFY) + - name: IMAGE_EXPIRES_AFTER + value: $(params.IMAGE_EXPIRES_AFTER) + - name: YUM_REPOS_D_SRC + value: $(params.YUM_REPOS_D_SRC) + - name: YUM_REPOS_D_FETCHED + value: $(params.YUM_REPOS_D_FETCHED) + - name: YUM_REPOS_D_TARGET + value: $(params.YUM_REPOS_D_TARGET) + - name: TARGET_STAGE + value: $(params.TARGET_STAGE) + - name: ENTITLEMENT_SECRET + value: $(params.ENTITLEMENT_SECRET) + - name: ACTIVATION_KEY + value: $(params.ACTIVATION_KEY) + - name: ADDITIONAL_SECRET + value: $(params.ADDITIONAL_SECRET) + - name: BUILD_ARGS_FILE + value: $(params.BUILD_ARGS_FILE) + - name: ADD_CAPABILITIES + value: $(params.ADD_CAPABILITIES) + - name: SQUASH + value: $(params.SQUASH) + - name: SKIP_UNUSED_STAGES + value: $(params.SKIP_UNUSED_STAGES) + - name: PRIVILEGED_NESTED + value: $(params.PRIVILEGED_NESTED) + - name: SKIP_SBOM_GENERATION + value: $(params.SKIP_SBOM_GENERATION) + volumeMounts: + - mountPath: /shared + name: shared + steps: + - args: + - --build-args + - $(params.BUILD_ARGS[*]) + - --labels + - $(params.LABELS[*]) + computeResources: + limits: + cpu: "20" + memory: 24Gi + requests: + cpu: "10" + memory: 20Gi + env: + - name: COMMIT_SHA + value: $(params.COMMIT_SHA) + - name: DOCKERFILE + value: $(params.DOCKERFILE) + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build + script: | + #!/bin/bash + set -euo pipefail + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + if [ -e "$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" + elif [ -e "$SOURCE_CODE_DIR/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$DOCKERFILE" + elif [ -e "$DOCKERFILE" ]; then + # Instrumented builds (SAST) use this custom dockerffile step as their base + dockerfile_path="$DOCKERFILE" + elif echo "$DOCKERFILE" | grep -q "^https\?://"; then + echo "Fetch Dockerfile from $DOCKERFILE" + dockerfile_path=$(mktemp --suffix=-Dockerfile) + http_code=$(curl -s -S -L -w "%{http_code}" --output "$dockerfile_path" "$DOCKERFILE") + if [ $http_code != 200 ]; then + echo "No Dockerfile is fetched. Server responds $http_code" + exit 1 + fi + http_code=$(curl -s -S -L -w "%{http_code}" --output "$dockerfile_path.dockerignore.tmp" "$DOCKERFILE.dockerignore") + if [ $http_code = 200 ]; then + echo "Fetched .dockerignore from $DOCKERFILE.dockerignore" + mv "$dockerfile_path.dockerignore.tmp" $SOURCE_CODE_DIR/$CONTEXT/.dockerignore + fi + else + echo "Cannot find Dockerfile $DOCKERFILE" + exit 1 + fi + + dockerfile_copy=$(mktemp --tmpdir "$(basename "$dockerfile_path").XXXXXX") + cp "$dockerfile_path" "$dockerfile_copy" + + if [ -n "${JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR-}" ] && grep -q '^\s*RUN \(./\)\?mvn' "$dockerfile_copy"; then + sed -i -e "s|^\s*RUN \(\(./\)\?mvn\)\(.*\)|RUN echo \"mirror.defaulthttp://$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR/v1/cache/default/0/*\" > /tmp/settings.yaml; \1 -s /tmp/settings.yaml \3|g" "$dockerfile_copy" + touch /var/lib/containers/java + fi + + # Fixing group permission on /var/lib/containers + chown root:root /var/lib/containers + + sed -i 's/^\s*short-name-mode\s*=\s*.*/short-name-mode = "disabled"/' /etc/containers/registries.conf + + # Setting new namespace to run buildah - 2^32-2 + echo 'root:1:4294967294' | tee -a /etc/subuid >> /etc/subgid + + build_args=() + if [ -n "${BUILD_ARGS_FILE}" ]; then + # Parse BUILD_ARGS_FILE ourselves because dockerfile-json doesn't support it + echo "Parsing ARGs from $BUILD_ARGS_FILE" + mapfile -t build_args < <( + # https://www.mankier.com/1/buildah-build#--build-arg-file + # delete lines that start with # + # delete blank lines + sed -e '/^#/d' -e '/^\s*$/d' "${SOURCE_CODE_DIR}/${BUILD_ARGS_FILE}" + ) + fi + + LABELS=() + # Split `args` into two sets of arguments. + while [[ $# -gt 0 ]]; do + case $1 in + --build-args) + shift + # Note: this may result in multiple --build-arg=KEY=value flags with the same KEY being + # passed to buildah. In that case, the *last* occurrence takes precedence. This is why + # we append BUILD_ARGS after the content of the BUILD_ARGS_FILE + while [[ $# -gt 0 && $1 != --* ]]; do build_args+=("$1"); shift; done + ;; + --labels) + shift + while [[ $# -gt 0 && $1 != --* ]]; do LABELS+=("--label" "$1"); shift; done + ;; + *) + echo "unexpected argument: $1" >&2 + exit 2 + ;; + esac + done + + BUILD_ARG_FLAGS=() + for build_arg in "${build_args[@]}"; do + BUILD_ARG_FLAGS+=("--build-arg=$build_arg") + done + + dockerfile-json "${BUILD_ARG_FLAGS[@]}" "$dockerfile_copy" > /shared/parsed_dockerfile.json + BASE_IMAGES=$( + jq -r '.Stages[] | select(.From | .Stage or .Scratch | not) | .BaseName | select(test("^oci-archive:") | not)' /shared/parsed_dockerfile.json + ) + + BUILDAH_ARGS=() + UNSHARE_ARGS=() + + if [ "${HERMETIC}" == "true" ]; then + BUILDAH_ARGS+=("--pull=never") + UNSHARE_ARGS+=("--net") + + for image in $BASE_IMAGES; do + unshare -Ufp --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -- buildah pull $image + done + echo "Build will be executed with network isolation" + fi + + if [ -n "${TARGET_STAGE}" ]; then + BUILDAH_ARGS+=("--target=${TARGET_STAGE}") + fi + + BUILDAH_ARGS+=("${BUILD_ARG_FLAGS[@]}") + + if [ "${PRIVILEGED_NESTED}" == "true" ]; then + BUILDAH_ARGS+=("--security-opt=label=disable") + BUILDAH_ARGS+=("--cap-add=all") + BUILDAH_ARGS+=("--device=/dev/fuse") + fi + + if [ -n "${ADD_CAPABILITIES}" ]; then + BUILDAH_ARGS+=("--cap-add=${ADD_CAPABILITIES}") + fi + + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + if [ "${SKIP_UNUSED_STAGES}" != "true" ] ; then + BUILDAH_ARGS+=("--skip-unused-stages=false") + fi + + VOLUME_MOUNTS=() + + if [ -f "$(workspaces.source.path)/cachi2/cachi2.env" ]; then + cp -r "$(workspaces.source.path)/cachi2" /tmp/ + chmod -R go+rwX /tmp/cachi2 + VOLUME_MOUNTS+=(--volume /tmp/cachi2:/cachi2) + # Read in the whole file (https://unix.stackexchange.com/questions/533277), then + # for each RUN ... line insert the cachi2.env command *after* any options like --mount + sed -E -i \ + -e 'H;1h;$!d;x' \ + -e 's@^\s*(run((\s|\\\n)+-\S+)*(\s|\\\n)+)@\1. /cachi2/cachi2.env \&\& \\\n @igM' \ + "$dockerfile_copy" + echo "Prefetched content will be made available" + + prefetched_repo_for_my_arch="/tmp/cachi2/output/deps/rpm/$(uname -m)/repos.d/cachi2.repo" + if [ -f "$prefetched_repo_for_my_arch" ]; then + echo "Adding $prefetched_repo_for_my_arch to $YUM_REPOS_D_FETCHED" + mkdir -p "$YUM_REPOS_D_FETCHED" + cp --no-clobber "$prefetched_repo_for_my_arch" "$YUM_REPOS_D_FETCHED" + fi + fi + + # if yum repofiles stored in git, copy them to mount point outside the source dir + if [ -d "${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}" ]; then + mkdir -p ${YUM_REPOS_D_FETCHED} + cp -r ${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}/* ${YUM_REPOS_D_FETCHED} + fi + + # if anything in the repofiles mount point (either fetched or from git), mount it + if [ -d "${YUM_REPOS_D_FETCHED}" ]; then + chmod -R go+rwX ${YUM_REPOS_D_FETCHED} + mount_point=$(realpath ${YUM_REPOS_D_FETCHED}) + VOLUME_MOUNTS+=(--volume "${mount_point}:${YUM_REPOS_D_TARGET}") + fi + + DEFAULT_LABELS=( + "--label" "build-date=$(date -u +'%Y-%m-%dT%H:%M:%S')" + "--label" "architecture=$(uname -m)" + "--label" "vcs-type=git" + ) + [ -n "$COMMIT_SHA" ] && DEFAULT_LABELS+=("--label" "vcs-ref=$COMMIT_SHA") + [ -n "$IMAGE_EXPIRES_AFTER" ] && DEFAULT_LABELS+=("--label" "quay.expires-after=$IMAGE_EXPIRES_AFTER") + + # Concatenate defaults and explicit labels. If a label appears twice, the last one wins. + LABELS=("${DEFAULT_LABELS[@]}" "${LABELS[@]}") + + ACTIVATION_KEY_PATH="/activation-key" + ENTITLEMENT_PATH="/entitlement" + + # 0. if hermetic=true, skip all subscription related stuff + # 1. do not enable activation key and entitlement at same time. If both vars are provided, prefer activation key. + # 2. Activation-keys will be used when the key 'org' exists in the activation key secret. + # 3. try to pre-register and mount files to the correct location so that users do no need to modify Dockerfiles. + # 3. If the Dockerfile contains the string "subcription-manager register", add the activation-keys volume + # to buildah but don't pre-register for backwards compatibility. Mount an empty directory on + # shared emptydir volume to "/etc/pki/entitlement" to prevent certificates from being included + + if [ "${HERMETIC}" != "true" ] && [ -e /activation-key/org ]; then + cp -r --preserve=mode "$ACTIVATION_KEY_PATH" /tmp/activation-key + mkdir -p /shared/rhsm/etc/pki/entitlement + mkdir -p /shared/rhsm/etc/pki/consumer + + VOLUME_MOUNTS+=(-v /tmp/activation-key:/activation-key \ + -v /shared/rhsm/etc/pki/entitlement:/etc/pki/entitlement:Z \ + -v /shared/rhsm/etc/pki/consumer:/etc/pki/consumer:Z) + echo "Adding activation key to the build" + + if ! grep -E "^[^#]*subscription-manager.[^#]*register" "$dockerfile_path"; then + # user is not running registration in the Containerfile: pre-register. + echo "Pre-registering with subscription manager." + subscription-manager register --org "$(cat /tmp/activation-key/org)" --activationkey "$(cat /tmp/activation-key/activationkey)" + trap 'subscription-manager unregister || true' EXIT + + # copy generated certificates to /shared volume + cp /etc/pki/entitlement/*.pem /shared/rhsm/etc/pki/entitlement + cp /etc/pki/consumer/*.pem /shared/rhsm/etc/pki/consumer + + # and then mount get /etc/rhsm/ca/redhat-uep.pem into /run/secrets/rhsm/ca + VOLUME_MOUNTS+=(--volume /etc/rhsm/ca/redhat-uep.pem:/etc/rhsm/ca/redhat-uep.pem:Z) + fi + + elif [ "${HERMETIC}" != "true" ] && find /entitlement -name "*.pem" >> null; then + cp -r --preserve=mode "$ENTITLEMENT_PATH" /tmp/entitlement + VOLUME_MOUNTS+=(--volume /tmp/entitlement:/etc/pki/entitlement) + echo "Adding the entitlement to the build" + fi + + if [ -n "${ADDITIONAL_VOLUME_MOUNTS-}" ]; then + # ADDITIONAL_VOLUME_MOUNTS allows to specify more volumes for the build. + # Instrumented builds (SAST) use this step as their base and add some other tools. + while read -r volume_mount; do + VOLUME_MOUNTS+=("--volume=$volume_mount") + done <<< "$ADDITIONAL_VOLUME_MOUNTS" + fi + + ADDITIONAL_SECRET_PATH="/additional-secret" + ADDITIONAL_SECRET_TMP="/tmp/additional-secret" + if [ -d "$ADDITIONAL_SECRET_PATH" ]; then + cp -r --preserve=mode -L "$ADDITIONAL_SECRET_PATH" $ADDITIONAL_SECRET_TMP + while read -r filename; do + echo "Adding the secret ${ADDITIONAL_SECRET}/${filename} to the build, available at /run/secrets/${ADDITIONAL_SECRET}/${filename}" + BUILDAH_ARGS+=("--secret=id=${ADDITIONAL_SECRET}/${filename},src=$ADDITIONAL_SECRET_TMP/${filename}") + done < <(find $ADDITIONAL_SECRET_TMP -maxdepth 1 -type f -exec basename {} \;) + fi + + # Prevent ShellCheck from giving a warning because 'image' is defined and 'IMAGE' is not. + declare IMAGE + + buildah_cmd_array=( + buildah build + "${VOLUME_MOUNTS[@]}" + "${BUILDAH_ARGS[@]}" + "${LABELS[@]}" + --tls-verify="$TLSVERIFY" --no-cache + --ulimit nofile=4096:4096 + -f "$dockerfile_copy" -t "$IMAGE" . + ) + buildah_cmd=$(printf "%q " "${buildah_cmd_array[@]}") + + if [ "${HERMETIC}" == "true" ]; then + # enabling loopback adapter enables Bazel builds to work in hermetic mode. + command="ip link set lo up && $buildah_cmd" + else + command="$buildah_cmd" + fi + + # disable host subcription manager integration + find /usr/share/rhel/secrets -type l -exec unlink {} \; + + unshare -Uf "${UNSHARE_ARGS[@]}" --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -w "${SOURCE_CODE_DIR}/$CONTEXT" -- sh -c "$command" + + container=$(buildah from --pull-never "$IMAGE") + + # Save the SBOM produced by Cachi2 so it can be merged into the final SBOM later + if [ -f "/tmp/cachi2/output/bom.json" ]; then + echo "Making copy of sbom-cachi2.json" + cp /tmp/cachi2/output/bom.json ./sbom-cachi2.json + fi + + buildah mount $container | tee /shared/container_path + # delete symlinks - they may point outside the container rootfs, messing with SBOM scanners + find $(cat /shared/container_path) -xtype l -delete + echo $container > /shared/container_name + + touch /shared/base_images_digests + echo "Recording base image digests used" + for image in $BASE_IMAGES; do + base_image_digest=$(buildah images --format '{{ .Name }}:{{ .Tag }}@{{ .Digest }}' --filter reference="$image") + # In some cases, there might be BASE_IMAGES, but not any associated digest. This happens + # if buildah did not use that particular image during build because it was skipped + if [ -n "$base_image_digest" ]; then + echo "$image $base_image_digest" | tee -a /shared/base_images_digests + fi + done + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /entitlement + name: etc-pki-entitlement + - mountPath: /activation-key + name: activation-key + - mountPath: /additional-secret + name: additional-secret + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/icm-injection-scripts:latest@sha256:523689a26c74790be67d6036820abfb9aa10bec6efff98cc1c9b74bd2f99eeb1 + name: icm + script: | + #!/bin/bash + set -euo pipefail + /scripts/inject-icm.sh "$IMAGE" + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: push + script: | + #!/bin/bash + set -e + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + retries=5 + # Push to a unique tag based on the TaskRun name to avoid race conditions + echo "Pushing to ${IMAGE%:*}:${TASKRUN_NAME}" + if ! buildah push \ + --retry "$retries" \ + --tls-verify="$TLSVERIFY" \ + "$IMAGE" \ + "docker://${IMAGE%:*}:$(context.taskRun.name)"; + then + echo "Failed to push sbom image to ${IMAGE%:*}:$(context.taskRun.name) after ${retries} tries" + exit 1 + fi + + # Push to a tag based on the git revision + echo "Pushing to ${IMAGE}" + if ! buildah push \ + --retry "$retries" \ + --tls-verify="$TLSVERIFY" \ + --digestfile "$(workspaces.source.path)/image-digest" "$IMAGE" \ + "docker://$IMAGE"; + then + echo "Failed to push sbom image to $IMAGE after ${retries} tries" + exit 1 + fi + + cat "$(workspaces.source.path)"/image-digest | tee $(results.IMAGE_DIGEST.path) + echo -n "$IMAGE" | tee $(results.IMAGE_URL.path) + { + echo -n "${IMAGE}@" + cat "$(workspaces.source.path)/image-digest" + } > "$(results.IMAGE_REF.path)" + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate + script: | + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + echo "Running syft on the source directory" + syft dir:"$(workspaces.source.path)/$SOURCE_CODE_DIR/$CONTEXT" --output cyclonedx-json@1.5="$(workspaces.source.path)/sbom-source.json" + echo "Running syft on the image filesystem" + syft dir:"$(cat /shared/container_path)" --output cyclonedx-json@1.5="$(workspaces.source.path)/sbom-image.json" + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + workingDir: $(workspaces.source.path)/source + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + name: analyse-dependencies-java-sbom + script: | + if [ -f /var/lib/containers/java ]; then + /opt/jboss/container/java/run/run-java.sh analyse-dependencies path $(cat /shared/container_path) -s $(workspaces.source.path)/sbom-image.json --task-run-name $(context.taskRun.name) --publishers $(results.SBOM_JAVA_COMPONENTS_COUNT.path) + sed -i 's/^/ /' $(results.SBOM_JAVA_COMPONENTS_COUNT.path) # Workaround for SRVKP-2875 + else + touch $(results.JAVA_COMMUNITY_DEPENDENCIES.path) + fi + securityContext: + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/sbom-utility-scripts@sha256:7f4ce55033077f37eda61c67b4289949471c36d23ae0cc66fd4113e615b3ef93 + name: prepare-sboms + script: | + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + echo "Merging contents of sbom-source.json and sbom-image.json into sbom-cyclonedx.json" + python3 /scripts/merge_syft_sboms.py + + if [ -f "sbom-cachi2.json" ]; then + echo "Merging contents of sbom-cachi2.json into sbom-cyclonedx.json" + python3 /scripts/merge_cachi2_sboms.py sbom-cachi2.json sbom-cyclonedx.json > sbom-temp.json + mv sbom-temp.json sbom-cyclonedx.json + fi + + echo "Adding base images data to sbom-cyclonedx.json" + python3 /scripts/base_images_sbom_script.py \ + --sbom=sbom-cyclonedx.json \ + --parsed-dockerfile=/shared/parsed_dockerfile.json \ + --base-images-digests=/shared/base_images_digests + + echo "Adding image reference to sbom" + IMAGE_URL="$(cat "$(results.IMAGE_URL.path)")" + IMAGE_DIGEST="$(cat "$(results.IMAGE_DIGEST.path)")" + + python3 /scripts/add_image_reference.py \ + --image-url "$IMAGE_URL" \ + --image-digest "$IMAGE_DIGEST" \ + --input-file sbom-cyclonedx.json \ + --output-file /tmp/sbom-cyclonedx.tmp.json + + mv /tmp/sbom-cyclonedx.tmp.json sbom-cyclonedx.json + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/appstudio-utils:48c311af02858e2422d6229600e9959e496ddef1@sha256:91ddd999271f65d8ec8487b10f3dd378f81aa894e11b9af4d10639fd52bba7e8 + name: upload-sbom + script: | + #!/bin/bash + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + cosign attach sbom --sbom sbom-cyclonedx.json --type cyclonedx "$(cat "$(results.IMAGE_REF.path)")" + + # Remove tag from IMAGE while allowing registry to contain a port number. + sbom_repo="${IMAGE%:*}" + sbom_digest="$(sha256sum sbom-cyclonedx.json | cut -d' ' -f1)" + # The SBOM_BLOB_URL is created by `cosign attach sbom`. + echo -n "${sbom_repo}@sha256:${sbom_digest}" | tee "$(results.SBOM_BLOB_URL.path)" + volumeMounts: + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + volumes: + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared + - name: etc-pki-entitlement + secret: + optional: true + secretName: $(params.ENTITLEMENT_SECRET) + - name: activation-key + secret: + optional: true + secretName: $(params.ACTIVATION_KEY) + - name: additional-secret + secret: + optional: true + secretName: $(params.ADDITIONAL_SECRET) + - configMap: + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) + optional: true + name: trusted-ca + workspaces: + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah-6gb/0.1/buildah-6gb.yaml b/task/buildah-6gb/0.1/buildah-6gb.yaml new file mode 100644 index 0000000000..2af92f8022 --- /dev/null +++ b/task/buildah-6gb/0.1/buildah-6gb.yaml @@ -0,0 +1,585 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2025-01-21T00:00:00Z" + build.appstudio.redhat.com/expiry-message: 'Instead of using this task, switch + to buildah task (you can find latest bundle reference here: https://quay.io/repository/konflux-ci/tekton-catalog/task-buildah) + and configure build step resources as described in the doc: https://konflux-ci.dev/docs/how-tos/configuring/overriding-compute-resources/' + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: "0.1" + build.appstudio.redhat.com/build_type: docker + name: buildah-6gb +spec: + description: |- + Buildah task builds source code into a container image and pushes the image into container registry using buildah tool. + In addition it generates a SBOM file, injects the SBOM file into final container image and pushes the SBOM file as separate image using cosign tool. + When [Java dependency rebuild](https://redhat-appstudio.github.io/docs.stonesoup.io/Documentation/main/cli/proc_enabled_java_dependencies.html) is enabled it triggers rebuilds of Java artifacts. + When prefetch-dependencies task was activated it is using its artifacts to run build in hermetic environment. + params: + - description: Reference of the image buildah will produce. + name: IMAGE + type: string + - default: "" + description: Deprecated. Has no effect. Will be removed in the future. + name: BUILDER_IMAGE + type: string + - default: ./Dockerfile + description: Path to the Dockerfile to build. + name: DOCKERFILE + type: string + - default: . + description: Path to the directory to use as context. + name: CONTEXT + type: string + - default: "true" + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) + name: TLSVERIFY + type: string + - default: "" + description: unused, should be removed in next task version + name: DOCKER_AUTH + type: string + - default: "false" + description: Determines if build will be executed without network access. + name: HERMETIC + type: string + - default: "" + description: In case it is not empty, the prefetched content should be made available + to the build. + name: PREFETCH_INPUT + type: string + - default: "" + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. + name: IMAGE_EXPIRES_AFTER + type: string + - default: "" + description: The image is built from this commit. + name: COMMIT_SHA + type: string + - default: repos.d + description: Path in the git repository in which yum repository files are stored + name: YUM_REPOS_D_SRC + - default: fetched.repos.d + description: Path in source workspace where dynamically-fetched repos are present + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE + type: string + - default: etc-pki-entitlement + description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET + type: string + - default: activation-key + description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY + type: string + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS + type: array + - default: "" + description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE + type: string + - default: trusted-ca + description: The name of the ConfigMap to read CA bundle data from. + name: caTrustConfigMapName + type: string + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey + type: string + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES + type: string + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH + type: string + - default: vfs + description: Storage driver to configure for buildah + name: STORAGE_DRIVER + type: string + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES + type: string + results: + - description: Digest of the image just built + name: IMAGE_DIGEST + - description: Image repository and tag where the built image was pushed + name: IMAGE_URL + - description: Digests of the base images used for build + name: BASE_IMAGES_DIGESTS + - description: The counting of Java components by publisher in JSON format + name: SBOM_JAVA_COMPONENTS_COUNT + type: string + - description: The Java dependencies that came from community sources such as Maven + central. + name: JAVA_COMMUNITY_DEPENDENCIES + stepTemplate: + env: + - name: BUILDAH_FORMAT + value: oci + - name: STORAGE_DRIVER + value: $(params.STORAGE_DRIVER) + - name: HERMETIC + value: $(params.HERMETIC) + - name: CONTEXT + value: $(params.CONTEXT) + - name: DOCKERFILE + value: $(params.DOCKERFILE) + - name: IMAGE + value: $(params.IMAGE) + - name: TLSVERIFY + value: $(params.TLSVERIFY) + - name: IMAGE_EXPIRES_AFTER + value: $(params.IMAGE_EXPIRES_AFTER) + - name: YUM_REPOS_D_SRC + value: $(params.YUM_REPOS_D_SRC) + - name: YUM_REPOS_D_FETCHED + value: $(params.YUM_REPOS_D_FETCHED) + - name: YUM_REPOS_D_TARGET + value: $(params.YUM_REPOS_D_TARGET) + - name: TARGET_STAGE + value: $(params.TARGET_STAGE) + - name: PARAM_BUILDER_IMAGE + value: $(params.BUILDER_IMAGE) + - name: ENTITLEMENT_SECRET + value: $(params.ENTITLEMENT_SECRET) + - name: ADDITIONAL_SECRET + value: $(params.ADDITIONAL_SECRET) + - name: BUILD_ARGS_FILE + value: $(params.BUILD_ARGS_FILE) + - name: ADD_CAPABILITIES + value: $(params.ADD_CAPABILITIES) + - name: SQUASH + value: $(params.SQUASH) + - name: SKIP_UNUSED_STAGES + value: $(params.SKIP_UNUSED_STAGES) + volumeMounts: + - mountPath: /shared + name: shared + steps: + - args: + - $(params.BUILD_ARGS[*]) + computeResources: + limits: + memory: 6Gi + requests: + cpu: 250m + memory: 4Gi + env: + - name: COMMIT_SHA + value: $(params.COMMIT_SHA) + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build + script: | + if [ -n "${PARAM_BUILDER_IMAGE}" ]; then + echo "WARNING: provided deprecated BUILDER_IMAGE parameter has no effect." + fi + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + SOURCE_CODE_DIR=source + if [ -e "$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" + elif [ -e "$SOURCE_CODE_DIR/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$DOCKERFILE" + elif echo "$DOCKERFILE" | grep -q "^https\?://"; then + echo "Fetch Dockerfile from $DOCKERFILE" + dockerfile_path=$(mktemp --suffix=-Dockerfile) + http_code=$(curl -s -L -w "%{http_code}" --output "$dockerfile_path" "$DOCKERFILE") + if [ $http_code != 200 ]; then + echo "No Dockerfile is fetched. Server responds $http_code" + exit 1 + fi + http_code=$(curl -s -L -w "%{http_code}" --output "$dockerfile_path.dockerignore.tmp" "$DOCKERFILE.dockerignore") + if [ $http_code = 200 ]; then + echo "Fetched .dockerignore from $DOCKERFILE.dockerignore" + mv "$dockerfile_path.dockerignore.tmp" $SOURCE_CODE_DIR/$CONTEXT/.dockerignore + fi + else + echo "Cannot find Dockerfile $DOCKERFILE" + exit 1 + fi + if [ -n "$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR" ] && grep -q '^\s*RUN \(./\)\?mvn' "$dockerfile_path"; then + sed -i -e "s|^\s*RUN \(\(./\)\?mvn\)\(.*\)|RUN echo \"mirror.defaulthttp://$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR/v1/cache/default/0/*\" > /tmp/settings.yaml; \1 -s /tmp/settings.yaml \3|g" "$dockerfile_path" + touch /var/lib/containers/java + fi + + # Fixing group permission on /var/lib/containers + chown root:root /var/lib/containers + + sed -i 's/^\s*short-name-mode\s*=\s*.*/short-name-mode = "disabled"/' /etc/containers/registries.conf + + # Setting new namespace to run buildah - 2^32-2 + echo 'root:1:4294967294' | tee -a /etc/subuid >> /etc/subgid + + BUILDAH_ARGS=() + + BASE_IMAGES=$(grep -i '^\s*FROM' "$dockerfile_path" | sed 's/--platform=\S*//' | awk '{print $2}' | (grep -v ^oci-archive: || true)) + if [ "${HERMETIC}" == "true" ]; then + BUILDAH_ARGS+=("--pull=never") + UNSHARE_ARGS="--net" + for image in $BASE_IMAGES; do + if [ "${image}" != "scratch" ]; then + unshare -Ufp --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -- buildah pull $image + fi + done + echo "Build will be executed with network isolation" + fi + + if [ -n "${TARGET_STAGE}" ]; then + BUILDAH_ARGS+=("--target=${TARGET_STAGE}") + fi + + if [ -n "${BUILD_ARGS_FILE}" ]; then + BUILDAH_ARGS+=("--build-arg-file=$(pwd)/$SOURCE_CODE_DIR/${BUILD_ARGS_FILE}") + fi + + for build_arg in "$@"; do + BUILDAH_ARGS+=("--build-arg=$build_arg") + done + + if [ -n "${ADD_CAPABILITIES}" ]; then + BUILDAH_ARGS+=("--cap-add=${ADD_CAPABILITIES}") + fi + + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + if [ "${SKIP_UNUSED_STAGES}" != "true" ] ; then + BUILDAH_ARGS+=("--skip-unused-stages=false") + fi + + if [ -f "$(workspaces.source.path)/cachi2/cachi2.env" ]; then + cp -r "$(workspaces.source.path)/cachi2" /tmp/ + chmod -R go+rwX /tmp/cachi2 + VOLUME_MOUNTS="--volume /tmp/cachi2:/cachi2" + # Read in the whole file (https://unix.stackexchange.com/questions/533277), then + # for each RUN ... line insert the cachi2.env command *after* any options like --mount + sed -E -i \ + -e 'H;1h;$!d;x' \ + -e 's@^\s*(run((\s|\\\n)+-\S+)*(\s|\\\n)+)@\1. /cachi2/cachi2.env \&\& \\\n @igM' \ + "$dockerfile_path" + echo "Prefetched content will be made available" + + prefetched_repo_for_my_arch="/tmp/cachi2/output/deps/rpm/$(uname -m)/repos.d/cachi2.repo" + if [ -f "$prefetched_repo_for_my_arch" ]; then + echo "Adding $prefetched_repo_for_my_arch to $YUM_REPOS_D_FETCHED" + mkdir -p "$YUM_REPOS_D_FETCHED" + cp --no-clobber "$prefetched_repo_for_my_arch" "$YUM_REPOS_D_FETCHED" + fi + fi + + # if yum repofiles stored in git, copy them to mount point outside the source dir + if [ -d "${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}" ]; then + mkdir -p ${YUM_REPOS_D_FETCHED} + cp -r ${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}/* ${YUM_REPOS_D_FETCHED} + fi + + # if anything in the repofiles mount point (either fetched or from git), mount it + if [ -d "${YUM_REPOS_D_FETCHED}" ]; then + chmod -R go+rwX ${YUM_REPOS_D_FETCHED} + mount_point=$(realpath ${YUM_REPOS_D_FETCHED}) + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume ${mount_point}:${YUM_REPOS_D_TARGET}" + fi + + LABELS=( + "--label" "build-date=$(date -u +'%Y-%m-%dT%H:%M:%S')" + "--label" "architecture=$(uname -m)" + "--label" "vcs-type=git" + ) + [ -n "$COMMIT_SHA" ] && LABELS+=("--label" "vcs-ref=$COMMIT_SHA") + [ -n "$IMAGE_EXPIRES_AFTER" ] && LABELS+=("--label" "quay.expires-after=$IMAGE_EXPIRES_AFTER") + + ACTIVATION_KEY_PATH="/activation-key" + ENTITLEMENT_PATH="/entitlement" + + if [ -e /activation-key/org ]; then + cp -r --preserve=mode "$ACTIVATION_KEY_PATH" /tmp/activation-key + mkdir /shared/rhsm-tmp + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume /tmp/activation-key:/activation-key -v /shared/rhsm-tmp:/etc/pki/entitlement:Z" + echo "Adding activation key to the build" + + elif find /entitlement -name "*.pem" >> null; then + cp -r --preserve=mode "$ENTITLEMENT_PATH" /tmp/entitlement + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume /tmp/entitlement:/etc/pki/entitlement" + echo "Adding the entitlement to the build" + fi + + ADDITIONAL_SECRET_PATH="/additional-secret" + ADDITIONAL_SECRET_TMP="/tmp/additional-secret" + if [ -d "$ADDITIONAL_SECRET_PATH" ]; then + cp -r --preserve=mode -L "$ADDITIONAL_SECRET_PATH" $ADDITIONAL_SECRET_TMP + while read -r filename; do + echo "Adding the secret ${ADDITIONAL_SECRET}/${filename} to the build, available at /run/secrets/${ADDITIONAL_SECRET}/${filename}" + BUILDAH_ARGS+=("--secret=id=${ADDITIONAL_SECRET}/${filename},src=$ADDITIONAL_SECRET_TMP/${filename}") + done < <(find $ADDITIONAL_SECRET_TMP -maxdepth 1 -type f -exec basename {} \;) + fi + + unshare -Uf $UNSHARE_ARGS --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -w ${SOURCE_CODE_DIR}/$CONTEXT -- buildah build \ + $VOLUME_MOUNTS \ + "${BUILDAH_ARGS[@]}" \ + "${LABELS[@]}" \ + --tls-verify=$TLSVERIFY --no-cache \ + --ulimit nofile=4096:4096 \ + -f "$dockerfile_path" -t $IMAGE . + + container=$(buildah from --pull-never $IMAGE) + buildah mount $container | tee /shared/container_path + # delete symlinks - they may point outside the container rootfs, messing with SBOM scanners + find $(cat /shared/container_path) -xtype l -delete + echo $container > /shared/container_name + + # Save the SBOM produced by Cachi2 so it can be merged into the final SBOM later + if [ -f "/tmp/cachi2/output/bom.json" ]; then + cp /tmp/cachi2/output/bom.json ./sbom-cachi2.json + fi + + # Expose base image digests + touch $(results.BASE_IMAGES_DIGESTS.path) + for image in $BASE_IMAGES; do + if [ "${image}" != "scratch" ]; then + buildah images --format '{{ .Name }}:{{ .Tag }}@{{ .Digest }}' --filter reference="$image" >> $(results.BASE_IMAGES_DIGESTS.path) + fi + done + + # Needed to generate base images SBOM + echo "$BASE_IMAGES" > $(workspaces.source.path)/base_images_from_dockerfile + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /entitlement + name: etc-pki-entitlement + - mountPath: /activation-key + name: activation-key + - mountPath: /additional-secret + name: additional-secret + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate + script: | + echo "Running syft on the source directory" + syft dir:$(workspaces.source.path)/source --output cyclonedx-json=$(workspaces.source.path)/sbom-source.json + echo "Running syft on the image filesystem" + syft dir:$(cat /shared/container_path) --output cyclonedx-json=$(workspaces.source.path)/sbom-image.json + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + workingDir: $(workspaces.source.path)/source + - image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + name: analyse-dependencies-java-sbom + script: | + if [ -f /var/lib/containers/java ]; then + /opt/jboss/container/java/run/run-java.sh analyse-dependencies path $(cat /shared/container_path) -s $(workspaces.source.path)/sbom-image.json --task-run-name $(context.taskRun.name) --publishers $(results.SBOM_JAVA_COMPONENTS_COUNT.path) + sed -i 's/^/ /' $(results.SBOM_JAVA_COMPONENTS_COUNT.path) # Workaround for SRVKP-2875 + else + touch $(results.JAVA_COMMUNITY_DEPENDENCIES.path) + fi + securityContext: + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + name: merge-syft-sboms + script: | + #!/bin/python3 + import json + + # load SBOMs + with open("./sbom-image.json") as f: + image_sbom = json.load(f) + + with open("./sbom-source.json") as f: + source_sbom = json.load(f) + + # fetch unique components from available SBOMs + def get_identifier(component): + return component["name"] + '@' + component.get("version", "") + + image_sbom_components = image_sbom.setdefault("components", []) + existing_components = [get_identifier(component) for component in image_sbom_components] + + source_sbom_components = source_sbom.get("components", []) + for component in source_sbom_components: + if get_identifier(component) not in existing_components: + image_sbom_components.append(component) + existing_components.append(get_identifier(component)) + + image_sbom_components.sort(key=lambda c: get_identifier(c)) + + # write the CycloneDX unified SBOM + with open("./sbom-cyclonedx.json", "w") as f: + json.dump(image_sbom, f, indent=4) + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/cachi2:0.17.0@sha256:963870f04aeb4a207e79b8eacb47e16c8faa7451f52e92959e0e16cdbd258fb3 + name: merge-cachi2-sbom + script: | + if [ -f "sbom-cachi2.json" ]; then + echo "Merging contents of sbom-cachi2.json into sbom-cyclonedx.json" + merge_syft_sbom sbom-cachi2.json sbom-cyclonedx.json > sbom-temp.json + mv sbom-temp.json sbom-cyclonedx.json + else + echo "Skipping step since no Cachi2 SBOM was produced" + fi + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + name: create-purl-sbom + script: | + #!/bin/python3 + import json + + with open("./sbom-cyclonedx.json") as f: + cyclonedx_sbom = json.load(f) + + purls = [{"purl": component["purl"]} for component in cyclonedx_sbom.get("components", []) if "purl" in component] + purl_content = {"image_contents": {"dependencies": purls}} + + with open("sbom-purl.json", "w") as output_file: + json.dump(purl_content, output_file, indent=4) + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - env: + - name: BASE_IMAGES_DIGESTS_PATH + value: $(results.BASE_IMAGES_DIGESTS.path) + image: quay.io/redhat-appstudio/base-images-sbom-script@sha256:667669e3def018f9dbb8eaf8868887a40bc07842221e9a98f6787edcff021840 + name: create-base-images-sbom + script: | + python3 /app/base_images_sbom_script.py --sbom=sbom-cyclonedx.json --base-images-from-dockerfile=base_images_from_dockerfile --base-images-digests=$BASE_IMAGES_DIGESTS_PATH + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - computeResources: {} + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: inject-sbom-and-push + script: | + if [ -n "${PARAM_BUILDER_IMAGE}" ]; then + echo "WARNING: provided deprecated BUILDER_IMAGE parameter has no effect." + fi + + base_image_name=$(buildah inspect --format '{{ index .ImageAnnotations "org.opencontainers.image.base.name"}}' $IMAGE | cut -f1 -d'@') + base_image_digest=$(buildah inspect --format '{{ index .ImageAnnotations "org.opencontainers.image.base.digest"}}' $IMAGE) + container=$(buildah from --pull-never $IMAGE) + buildah copy $container sbom-cyclonedx.json sbom-purl.json /root/buildinfo/content_manifests/ + buildah config -a org.opencontainers.image.base.name=${base_image_name} -a org.opencontainers.image.base.digest=${base_image_digest} $container + + BUILDAH_ARGS=() + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + buildah commit "${BUILDAH_ARGS[@]}" $container $IMAGE + + status=-1 + max_run=5 + sleep_sec=10 + for run in $(seq 1 $max_run); do + status=0 + [ "$run" -gt 1 ] && sleep $sleep_sec + echo "Pushing sbom image to registry" + buildah push \ + --tls-verify=$TLSVERIFY \ + --digestfile $(workspaces.source.path)/image-digest $IMAGE \ + docker://$IMAGE && break || status=$? + done + if [ "$status" -ne 0 ]; then + echo "Failed to push sbom image to registry after ${max_run} tries" + exit 1 + fi + + cat "$(workspaces.source.path)"/image-digest | tee $(results.IMAGE_DIGEST.path) + echo -n "$IMAGE" | tee $(results.IMAGE_URL.path) + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + workingDir: $(workspaces.source.path) + - args: + - attach + - sbom + - --sbom + - sbom-cyclonedx.json + - --type + - cyclonedx + - $(params.IMAGE) + image: quay.io/redhat-appstudio/cosign:v2.1.1@sha256:c883d6f8d39148f2cea71bff4622d196d89df3e510f36c140c097b932f0dd5d5 + name: upload-sbom + workingDir: $(workspaces.source.path) + volumes: + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared + - name: etc-pki-entitlement + secret: + optional: true + secretName: $(params.ENTITLEMENT_SECRET) + - name: activation-key + secret: + optional: true + secretName: $(params.ACTIVATION_KEY) + - name: additional-secret + secret: + optional: true + secretName: $(params.ADDITIONAL_SECRET) + - configMap: + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) + optional: true + name: trusted-ca + workspaces: + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah-6gb/0.2/buildah-6gb.yaml b/task/buildah-6gb/0.2/buildah-6gb.yaml new file mode 100644 index 0000000000..2f3a43fd6e --- /dev/null +++ b/task/buildah-6gb/0.2/buildah-6gb.yaml @@ -0,0 +1,742 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2025-01-21T00:00:00Z" + build.appstudio.redhat.com/expiry-message: 'Instead of using this task, switch + to buildah task (you can find latest bundle reference here: https://quay.io/repository/konflux-ci/tekton-catalog/task-buildah) + and configure build step resources as described in the doc: https://konflux-ci.dev/docs/how-tos/configuring/overriding-compute-resources/' + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: 0.2.1 + build.appstudio.redhat.com/build_type: docker + name: buildah-6gb +spec: + description: |- + Buildah task builds source code into a container image and pushes the image into container registry using buildah tool. + In addition it generates a SBOM file, injects the SBOM file into final container image and pushes the SBOM file as separate image using cosign tool. + When [Java dependency rebuild](https://redhat-appstudio.github.io/docs.stonesoup.io/Documentation/main/cli/proc_enabled_java_dependencies.html) is enabled it triggers rebuilds of Java artifacts. + When prefetch-dependencies task was activated it is using its artifacts to run build in hermetic environment. + params: + - description: Reference of the image buildah will produce. + name: IMAGE + type: string + - default: ./Dockerfile + description: Path to the Dockerfile to build. + name: DOCKERFILE + type: string + - default: . + description: Path to the directory to use as context. + name: CONTEXT + type: string + - default: "true" + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) + name: TLSVERIFY + type: string + - default: "false" + description: Determines if build will be executed without network access. + name: HERMETIC + type: string + - default: "" + description: In case it is not empty, the prefetched content should be made available + to the build. + name: PREFETCH_INPUT + type: string + - default: "" + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. + name: IMAGE_EXPIRES_AFTER + type: string + - default: "" + description: The image is built from this commit. + name: COMMIT_SHA + type: string + - default: repos.d + description: Path in the git repository in which yum repository files are stored + name: YUM_REPOS_D_SRC + - default: fetched.repos.d + description: Path in source workspace where dynamically-fetched repos are present + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE + type: string + - default: etc-pki-entitlement + description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET + type: string + - default: activation-key + description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY + type: string + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS + type: array + - default: "" + description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE + type: string + - default: trusted-ca + description: The name of the ConfigMap to read CA bundle data from. + name: caTrustConfigMapName + type: string + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey + type: string + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES + type: string + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH + type: string + - default: vfs + description: Storage driver to configure for buildah + name: STORAGE_DRIVER + type: string + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES + type: string + - default: [] + description: Additional key=value labels that should be applied to the image + name: LABELS + type: array + - default: "false" + description: Whether to enable privileged mode + name: PRIVILEGED_NESTED + type: string + - default: "false" + description: Skip SBOM-related operations. This will likely cause EC policies + to fail if enabled + name: SKIP_SBOM_GENERATION + type: string + results: + - description: Digest of the image just built + name: IMAGE_DIGEST + - description: Image repository and tag where the built image was pushed + name: IMAGE_URL + - description: Image reference of the built image + name: IMAGE_REF + - description: Reference of SBOM blob digest to enable digest-based verification + from provenance + name: SBOM_BLOB_URL + type: string + - description: The counting of Java components by publisher in JSON format + name: SBOM_JAVA_COMPONENTS_COUNT + type: string + - description: The Java dependencies that came from community sources such as Maven + central. + name: JAVA_COMMUNITY_DEPENDENCIES + stepTemplate: + computeResources: + limits: + cpu: "4" + memory: 4Gi + requests: + cpu: "1" + memory: 1Gi + env: + - name: BUILDAH_FORMAT + value: oci + - name: STORAGE_DRIVER + value: $(params.STORAGE_DRIVER) + - name: HERMETIC + value: $(params.HERMETIC) + - name: SOURCE_CODE_DIR + value: source + - name: CONTEXT + value: $(params.CONTEXT) + - name: IMAGE + value: $(params.IMAGE) + - name: TLSVERIFY + value: $(params.TLSVERIFY) + - name: IMAGE_EXPIRES_AFTER + value: $(params.IMAGE_EXPIRES_AFTER) + - name: YUM_REPOS_D_SRC + value: $(params.YUM_REPOS_D_SRC) + - name: YUM_REPOS_D_FETCHED + value: $(params.YUM_REPOS_D_FETCHED) + - name: YUM_REPOS_D_TARGET + value: $(params.YUM_REPOS_D_TARGET) + - name: TARGET_STAGE + value: $(params.TARGET_STAGE) + - name: ENTITLEMENT_SECRET + value: $(params.ENTITLEMENT_SECRET) + - name: ACTIVATION_KEY + value: $(params.ACTIVATION_KEY) + - name: ADDITIONAL_SECRET + value: $(params.ADDITIONAL_SECRET) + - name: BUILD_ARGS_FILE + value: $(params.BUILD_ARGS_FILE) + - name: ADD_CAPABILITIES + value: $(params.ADD_CAPABILITIES) + - name: SQUASH + value: $(params.SQUASH) + - name: SKIP_UNUSED_STAGES + value: $(params.SKIP_UNUSED_STAGES) + - name: PRIVILEGED_NESTED + value: $(params.PRIVILEGED_NESTED) + - name: SKIP_SBOM_GENERATION + value: $(params.SKIP_SBOM_GENERATION) + volumeMounts: + - mountPath: /shared + name: shared + steps: + - args: + - --build-args + - $(params.BUILD_ARGS[*]) + - --labels + - $(params.LABELS[*]) + computeResources: + limits: + cpu: "4" + memory: 6Gi + requests: + cpu: "1" + memory: 4Gi + env: + - name: COMMIT_SHA + value: $(params.COMMIT_SHA) + - name: DOCKERFILE + value: $(params.DOCKERFILE) + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build + script: | + #!/bin/bash + set -euo pipefail + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + if [ -e "$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" + elif [ -e "$SOURCE_CODE_DIR/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$DOCKERFILE" + elif [ -e "$DOCKERFILE" ]; then + # Instrumented builds (SAST) use this custom dockerffile step as their base + dockerfile_path="$DOCKERFILE" + elif echo "$DOCKERFILE" | grep -q "^https\?://"; then + echo "Fetch Dockerfile from $DOCKERFILE" + dockerfile_path=$(mktemp --suffix=-Dockerfile) + http_code=$(curl -s -S -L -w "%{http_code}" --output "$dockerfile_path" "$DOCKERFILE") + if [ $http_code != 200 ]; then + echo "No Dockerfile is fetched. Server responds $http_code" + exit 1 + fi + http_code=$(curl -s -S -L -w "%{http_code}" --output "$dockerfile_path.dockerignore.tmp" "$DOCKERFILE.dockerignore") + if [ $http_code = 200 ]; then + echo "Fetched .dockerignore from $DOCKERFILE.dockerignore" + mv "$dockerfile_path.dockerignore.tmp" $SOURCE_CODE_DIR/$CONTEXT/.dockerignore + fi + else + echo "Cannot find Dockerfile $DOCKERFILE" + exit 1 + fi + + dockerfile_copy=$(mktemp --tmpdir "$(basename "$dockerfile_path").XXXXXX") + cp "$dockerfile_path" "$dockerfile_copy" + + if [ -n "${JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR-}" ] && grep -q '^\s*RUN \(./\)\?mvn' "$dockerfile_copy"; then + sed -i -e "s|^\s*RUN \(\(./\)\?mvn\)\(.*\)|RUN echo \"mirror.defaulthttp://$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR/v1/cache/default/0/*\" > /tmp/settings.yaml; \1 -s /tmp/settings.yaml \3|g" "$dockerfile_copy" + touch /var/lib/containers/java + fi + + # Fixing group permission on /var/lib/containers + chown root:root /var/lib/containers + + sed -i 's/^\s*short-name-mode\s*=\s*.*/short-name-mode = "disabled"/' /etc/containers/registries.conf + + # Setting new namespace to run buildah - 2^32-2 + echo 'root:1:4294967294' | tee -a /etc/subuid >> /etc/subgid + + build_args=() + if [ -n "${BUILD_ARGS_FILE}" ]; then + # Parse BUILD_ARGS_FILE ourselves because dockerfile-json doesn't support it + echo "Parsing ARGs from $BUILD_ARGS_FILE" + mapfile -t build_args < <( + # https://www.mankier.com/1/buildah-build#--build-arg-file + # delete lines that start with # + # delete blank lines + sed -e '/^#/d' -e '/^\s*$/d' "${SOURCE_CODE_DIR}/${BUILD_ARGS_FILE}" + ) + fi + + LABELS=() + # Split `args` into two sets of arguments. + while [[ $# -gt 0 ]]; do + case $1 in + --build-args) + shift + # Note: this may result in multiple --build-arg=KEY=value flags with the same KEY being + # passed to buildah. In that case, the *last* occurrence takes precedence. This is why + # we append BUILD_ARGS after the content of the BUILD_ARGS_FILE + while [[ $# -gt 0 && $1 != --* ]]; do build_args+=("$1"); shift; done + ;; + --labels) + shift + while [[ $# -gt 0 && $1 != --* ]]; do LABELS+=("--label" "$1"); shift; done + ;; + *) + echo "unexpected argument: $1" >&2 + exit 2 + ;; + esac + done + + BUILD_ARG_FLAGS=() + for build_arg in "${build_args[@]}"; do + BUILD_ARG_FLAGS+=("--build-arg=$build_arg") + done + + dockerfile-json "${BUILD_ARG_FLAGS[@]}" "$dockerfile_copy" > /shared/parsed_dockerfile.json + BASE_IMAGES=$( + jq -r '.Stages[] | select(.From | .Stage or .Scratch | not) | .BaseName | select(test("^oci-archive:") | not)' /shared/parsed_dockerfile.json + ) + + BUILDAH_ARGS=() + UNSHARE_ARGS=() + + if [ "${HERMETIC}" == "true" ]; then + BUILDAH_ARGS+=("--pull=never") + UNSHARE_ARGS+=("--net") + + for image in $BASE_IMAGES; do + unshare -Ufp --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -- buildah pull $image + done + echo "Build will be executed with network isolation" + fi + + if [ -n "${TARGET_STAGE}" ]; then + BUILDAH_ARGS+=("--target=${TARGET_STAGE}") + fi + + BUILDAH_ARGS+=("${BUILD_ARG_FLAGS[@]}") + + if [ "${PRIVILEGED_NESTED}" == "true" ]; then + BUILDAH_ARGS+=("--security-opt=label=disable") + BUILDAH_ARGS+=("--cap-add=all") + BUILDAH_ARGS+=("--device=/dev/fuse") + fi + + if [ -n "${ADD_CAPABILITIES}" ]; then + BUILDAH_ARGS+=("--cap-add=${ADD_CAPABILITIES}") + fi + + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + if [ "${SKIP_UNUSED_STAGES}" != "true" ] ; then + BUILDAH_ARGS+=("--skip-unused-stages=false") + fi + + VOLUME_MOUNTS=() + + if [ -f "$(workspaces.source.path)/cachi2/cachi2.env" ]; then + cp -r "$(workspaces.source.path)/cachi2" /tmp/ + chmod -R go+rwX /tmp/cachi2 + VOLUME_MOUNTS+=(--volume /tmp/cachi2:/cachi2) + # Read in the whole file (https://unix.stackexchange.com/questions/533277), then + # for each RUN ... line insert the cachi2.env command *after* any options like --mount + sed -E -i \ + -e 'H;1h;$!d;x' \ + -e 's@^\s*(run((\s|\\\n)+-\S+)*(\s|\\\n)+)@\1. /cachi2/cachi2.env \&\& \\\n @igM' \ + "$dockerfile_copy" + echo "Prefetched content will be made available" + + prefetched_repo_for_my_arch="/tmp/cachi2/output/deps/rpm/$(uname -m)/repos.d/cachi2.repo" + if [ -f "$prefetched_repo_for_my_arch" ]; then + echo "Adding $prefetched_repo_for_my_arch to $YUM_REPOS_D_FETCHED" + mkdir -p "$YUM_REPOS_D_FETCHED" + cp --no-clobber "$prefetched_repo_for_my_arch" "$YUM_REPOS_D_FETCHED" + fi + fi + + # if yum repofiles stored in git, copy them to mount point outside the source dir + if [ -d "${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}" ]; then + mkdir -p ${YUM_REPOS_D_FETCHED} + cp -r ${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}/* ${YUM_REPOS_D_FETCHED} + fi + + # if anything in the repofiles mount point (either fetched or from git), mount it + if [ -d "${YUM_REPOS_D_FETCHED}" ]; then + chmod -R go+rwX ${YUM_REPOS_D_FETCHED} + mount_point=$(realpath ${YUM_REPOS_D_FETCHED}) + VOLUME_MOUNTS+=(--volume "${mount_point}:${YUM_REPOS_D_TARGET}") + fi + + DEFAULT_LABELS=( + "--label" "build-date=$(date -u +'%Y-%m-%dT%H:%M:%S')" + "--label" "architecture=$(uname -m)" + "--label" "vcs-type=git" + ) + [ -n "$COMMIT_SHA" ] && DEFAULT_LABELS+=("--label" "vcs-ref=$COMMIT_SHA") + [ -n "$IMAGE_EXPIRES_AFTER" ] && DEFAULT_LABELS+=("--label" "quay.expires-after=$IMAGE_EXPIRES_AFTER") + + # Concatenate defaults and explicit labels. If a label appears twice, the last one wins. + LABELS=("${DEFAULT_LABELS[@]}" "${LABELS[@]}") + + ACTIVATION_KEY_PATH="/activation-key" + ENTITLEMENT_PATH="/entitlement" + + # 0. if hermetic=true, skip all subscription related stuff + # 1. do not enable activation key and entitlement at same time. If both vars are provided, prefer activation key. + # 2. Activation-keys will be used when the key 'org' exists in the activation key secret. + # 3. try to pre-register and mount files to the correct location so that users do no need to modify Dockerfiles. + # 3. If the Dockerfile contains the string "subcription-manager register", add the activation-keys volume + # to buildah but don't pre-register for backwards compatibility. Mount an empty directory on + # shared emptydir volume to "/etc/pki/entitlement" to prevent certificates from being included + + if [ "${HERMETIC}" != "true" ] && [ -e /activation-key/org ]; then + cp -r --preserve=mode "$ACTIVATION_KEY_PATH" /tmp/activation-key + mkdir -p /shared/rhsm/etc/pki/entitlement + mkdir -p /shared/rhsm/etc/pki/consumer + + VOLUME_MOUNTS+=(-v /tmp/activation-key:/activation-key \ + -v /shared/rhsm/etc/pki/entitlement:/etc/pki/entitlement:Z \ + -v /shared/rhsm/etc/pki/consumer:/etc/pki/consumer:Z) + echo "Adding activation key to the build" + + if ! grep -E "^[^#]*subscription-manager.[^#]*register" "$dockerfile_path"; then + # user is not running registration in the Containerfile: pre-register. + echo "Pre-registering with subscription manager." + subscription-manager register --org "$(cat /tmp/activation-key/org)" --activationkey "$(cat /tmp/activation-key/activationkey)" + trap 'subscription-manager unregister || true' EXIT + + # copy generated certificates to /shared volume + cp /etc/pki/entitlement/*.pem /shared/rhsm/etc/pki/entitlement + cp /etc/pki/consumer/*.pem /shared/rhsm/etc/pki/consumer + + # and then mount get /etc/rhsm/ca/redhat-uep.pem into /run/secrets/rhsm/ca + VOLUME_MOUNTS+=(--volume /etc/rhsm/ca/redhat-uep.pem:/etc/rhsm/ca/redhat-uep.pem:Z) + fi + + elif [ "${HERMETIC}" != "true" ] && find /entitlement -name "*.pem" >> null; then + cp -r --preserve=mode "$ENTITLEMENT_PATH" /tmp/entitlement + VOLUME_MOUNTS+=(--volume /tmp/entitlement:/etc/pki/entitlement) + echo "Adding the entitlement to the build" + fi + + if [ -n "${ADDITIONAL_VOLUME_MOUNTS-}" ]; then + # ADDITIONAL_VOLUME_MOUNTS allows to specify more volumes for the build. + # Instrumented builds (SAST) use this step as their base and add some other tools. + while read -r volume_mount; do + VOLUME_MOUNTS+=("--volume=$volume_mount") + done <<< "$ADDITIONAL_VOLUME_MOUNTS" + fi + + ADDITIONAL_SECRET_PATH="/additional-secret" + ADDITIONAL_SECRET_TMP="/tmp/additional-secret" + if [ -d "$ADDITIONAL_SECRET_PATH" ]; then + cp -r --preserve=mode -L "$ADDITIONAL_SECRET_PATH" $ADDITIONAL_SECRET_TMP + while read -r filename; do + echo "Adding the secret ${ADDITIONAL_SECRET}/${filename} to the build, available at /run/secrets/${ADDITIONAL_SECRET}/${filename}" + BUILDAH_ARGS+=("--secret=id=${ADDITIONAL_SECRET}/${filename},src=$ADDITIONAL_SECRET_TMP/${filename}") + done < <(find $ADDITIONAL_SECRET_TMP -maxdepth 1 -type f -exec basename {} \;) + fi + + # Prevent ShellCheck from giving a warning because 'image' is defined and 'IMAGE' is not. + declare IMAGE + + buildah_cmd_array=( + buildah build + "${VOLUME_MOUNTS[@]}" + "${BUILDAH_ARGS[@]}" + "${LABELS[@]}" + --tls-verify="$TLSVERIFY" --no-cache + --ulimit nofile=4096:4096 + -f "$dockerfile_copy" -t "$IMAGE" . + ) + buildah_cmd=$(printf "%q " "${buildah_cmd_array[@]}") + + if [ "${HERMETIC}" == "true" ]; then + # enabling loopback adapter enables Bazel builds to work in hermetic mode. + command="ip link set lo up && $buildah_cmd" + else + command="$buildah_cmd" + fi + + # disable host subcription manager integration + find /usr/share/rhel/secrets -type l -exec unlink {} \; + + unshare -Uf "${UNSHARE_ARGS[@]}" --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -w "${SOURCE_CODE_DIR}/$CONTEXT" -- sh -c "$command" + + container=$(buildah from --pull-never "$IMAGE") + + # Save the SBOM produced by Cachi2 so it can be merged into the final SBOM later + if [ -f "/tmp/cachi2/output/bom.json" ]; then + echo "Making copy of sbom-cachi2.json" + cp /tmp/cachi2/output/bom.json ./sbom-cachi2.json + fi + + buildah mount $container | tee /shared/container_path + # delete symlinks - they may point outside the container rootfs, messing with SBOM scanners + find $(cat /shared/container_path) -xtype l -delete + echo $container > /shared/container_name + + touch /shared/base_images_digests + echo "Recording base image digests used" + for image in $BASE_IMAGES; do + base_image_digest=$(buildah images --format '{{ .Name }}:{{ .Tag }}@{{ .Digest }}' --filter reference="$image") + # In some cases, there might be BASE_IMAGES, but not any associated digest. This happens + # if buildah did not use that particular image during build because it was skipped + if [ -n "$base_image_digest" ]; then + echo "$image $base_image_digest" | tee -a /shared/base_images_digests + fi + done + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /entitlement + name: etc-pki-entitlement + - mountPath: /activation-key + name: activation-key + - mountPath: /additional-secret + name: additional-secret + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/icm-injection-scripts:latest@sha256:523689a26c74790be67d6036820abfb9aa10bec6efff98cc1c9b74bd2f99eeb1 + name: icm + script: | + #!/bin/bash + set -euo pipefail + /scripts/inject-icm.sh "$IMAGE" + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: push + script: | + #!/bin/bash + set -e + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + retries=5 + # Push to a unique tag based on the TaskRun name to avoid race conditions + echo "Pushing to ${IMAGE%:*}:${TASKRUN_NAME}" + if ! buildah push \ + --retry "$retries" \ + --tls-verify="$TLSVERIFY" \ + "$IMAGE" \ + "docker://${IMAGE%:*}:$(context.taskRun.name)"; + then + echo "Failed to push sbom image to ${IMAGE%:*}:$(context.taskRun.name) after ${retries} tries" + exit 1 + fi + + # Push to a tag based on the git revision + echo "Pushing to ${IMAGE}" + if ! buildah push \ + --retry "$retries" \ + --tls-verify="$TLSVERIFY" \ + --digestfile "$(workspaces.source.path)/image-digest" "$IMAGE" \ + "docker://$IMAGE"; + then + echo "Failed to push sbom image to $IMAGE after ${retries} tries" + exit 1 + fi + + cat "$(workspaces.source.path)"/image-digest | tee $(results.IMAGE_DIGEST.path) + echo -n "$IMAGE" | tee $(results.IMAGE_URL.path) + { + echo -n "${IMAGE}@" + cat "$(workspaces.source.path)/image-digest" + } > "$(results.IMAGE_REF.path)" + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate + script: | + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + echo "Running syft on the source directory" + syft dir:"$(workspaces.source.path)/$SOURCE_CODE_DIR/$CONTEXT" --output cyclonedx-json@1.5="$(workspaces.source.path)/sbom-source.json" + echo "Running syft on the image filesystem" + syft dir:"$(cat /shared/container_path)" --output cyclonedx-json@1.5="$(workspaces.source.path)/sbom-image.json" + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + workingDir: $(workspaces.source.path)/source + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + name: analyse-dependencies-java-sbom + script: | + if [ -f /var/lib/containers/java ]; then + /opt/jboss/container/java/run/run-java.sh analyse-dependencies path $(cat /shared/container_path) -s $(workspaces.source.path)/sbom-image.json --task-run-name $(context.taskRun.name) --publishers $(results.SBOM_JAVA_COMPONENTS_COUNT.path) + sed -i 's/^/ /' $(results.SBOM_JAVA_COMPONENTS_COUNT.path) # Workaround for SRVKP-2875 + else + touch $(results.JAVA_COMMUNITY_DEPENDENCIES.path) + fi + securityContext: + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/sbom-utility-scripts@sha256:7f4ce55033077f37eda61c67b4289949471c36d23ae0cc66fd4113e615b3ef93 + name: prepare-sboms + script: | + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + echo "Merging contents of sbom-source.json and sbom-image.json into sbom-cyclonedx.json" + python3 /scripts/merge_syft_sboms.py + + if [ -f "sbom-cachi2.json" ]; then + echo "Merging contents of sbom-cachi2.json into sbom-cyclonedx.json" + python3 /scripts/merge_cachi2_sboms.py sbom-cachi2.json sbom-cyclonedx.json > sbom-temp.json + mv sbom-temp.json sbom-cyclonedx.json + fi + + echo "Adding base images data to sbom-cyclonedx.json" + python3 /scripts/base_images_sbom_script.py \ + --sbom=sbom-cyclonedx.json \ + --parsed-dockerfile=/shared/parsed_dockerfile.json \ + --base-images-digests=/shared/base_images_digests + + echo "Adding image reference to sbom" + IMAGE_URL="$(cat "$(results.IMAGE_URL.path)")" + IMAGE_DIGEST="$(cat "$(results.IMAGE_DIGEST.path)")" + + python3 /scripts/add_image_reference.py \ + --image-url "$IMAGE_URL" \ + --image-digest "$IMAGE_DIGEST" \ + --input-file sbom-cyclonedx.json \ + --output-file /tmp/sbom-cyclonedx.tmp.json + + mv /tmp/sbom-cyclonedx.tmp.json sbom-cyclonedx.json + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/appstudio-utils:48c311af02858e2422d6229600e9959e496ddef1@sha256:91ddd999271f65d8ec8487b10f3dd378f81aa894e11b9af4d10639fd52bba7e8 + name: upload-sbom + script: | + #!/bin/bash + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + cosign attach sbom --sbom sbom-cyclonedx.json --type cyclonedx "$(cat "$(results.IMAGE_REF.path)")" + + # Remove tag from IMAGE while allowing registry to contain a port number. + sbom_repo="${IMAGE%:*}" + sbom_digest="$(sha256sum sbom-cyclonedx.json | cut -d' ' -f1)" + # The SBOM_BLOB_URL is created by `cosign attach sbom`. + echo -n "${sbom_repo}@sha256:${sbom_digest}" | tee "$(results.SBOM_BLOB_URL.path)" + volumeMounts: + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + volumes: + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared + - name: etc-pki-entitlement + secret: + optional: true + secretName: $(params.ENTITLEMENT_SECRET) + - name: activation-key + secret: + optional: true + secretName: $(params.ACTIVATION_KEY) + - name: additional-secret + secret: + optional: true + secretName: $(params.ADDITIONAL_SECRET) + - configMap: + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) + optional: true + name: trusted-ca + workspaces: + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah-8gb/0.1/buildah-8gb.yaml b/task/buildah-8gb/0.1/buildah-8gb.yaml new file mode 100644 index 0000000000..b926dbe5c6 --- /dev/null +++ b/task/buildah-8gb/0.1/buildah-8gb.yaml @@ -0,0 +1,585 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2025-01-21T00:00:00Z" + build.appstudio.redhat.com/expiry-message: 'Instead of using this task, switch + to buildah task (you can find latest bundle reference here: https://quay.io/repository/konflux-ci/tekton-catalog/task-buildah) + and configure build step resources as described in the doc: https://konflux-ci.dev/docs/how-tos/configuring/overriding-compute-resources/' + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: "0.1" + build.appstudio.redhat.com/build_type: docker + name: buildah-8gb +spec: + description: |- + Buildah task builds source code into a container image and pushes the image into container registry using buildah tool. + In addition it generates a SBOM file, injects the SBOM file into final container image and pushes the SBOM file as separate image using cosign tool. + When [Java dependency rebuild](https://redhat-appstudio.github.io/docs.stonesoup.io/Documentation/main/cli/proc_enabled_java_dependencies.html) is enabled it triggers rebuilds of Java artifacts. + When prefetch-dependencies task was activated it is using its artifacts to run build in hermetic environment. + params: + - description: Reference of the image buildah will produce. + name: IMAGE + type: string + - default: "" + description: Deprecated. Has no effect. Will be removed in the future. + name: BUILDER_IMAGE + type: string + - default: ./Dockerfile + description: Path to the Dockerfile to build. + name: DOCKERFILE + type: string + - default: . + description: Path to the directory to use as context. + name: CONTEXT + type: string + - default: "true" + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) + name: TLSVERIFY + type: string + - default: "" + description: unused, should be removed in next task version + name: DOCKER_AUTH + type: string + - default: "false" + description: Determines if build will be executed without network access. + name: HERMETIC + type: string + - default: "" + description: In case it is not empty, the prefetched content should be made available + to the build. + name: PREFETCH_INPUT + type: string + - default: "" + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. + name: IMAGE_EXPIRES_AFTER + type: string + - default: "" + description: The image is built from this commit. + name: COMMIT_SHA + type: string + - default: repos.d + description: Path in the git repository in which yum repository files are stored + name: YUM_REPOS_D_SRC + - default: fetched.repos.d + description: Path in source workspace where dynamically-fetched repos are present + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE + type: string + - default: etc-pki-entitlement + description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET + type: string + - default: activation-key + description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY + type: string + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS + type: array + - default: "" + description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE + type: string + - default: trusted-ca + description: The name of the ConfigMap to read CA bundle data from. + name: caTrustConfigMapName + type: string + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey + type: string + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES + type: string + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH + type: string + - default: vfs + description: Storage driver to configure for buildah + name: STORAGE_DRIVER + type: string + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES + type: string + results: + - description: Digest of the image just built + name: IMAGE_DIGEST + - description: Image repository and tag where the built image was pushed + name: IMAGE_URL + - description: Digests of the base images used for build + name: BASE_IMAGES_DIGESTS + - description: The counting of Java components by publisher in JSON format + name: SBOM_JAVA_COMPONENTS_COUNT + type: string + - description: The Java dependencies that came from community sources such as Maven + central. + name: JAVA_COMMUNITY_DEPENDENCIES + stepTemplate: + env: + - name: BUILDAH_FORMAT + value: oci + - name: STORAGE_DRIVER + value: $(params.STORAGE_DRIVER) + - name: HERMETIC + value: $(params.HERMETIC) + - name: CONTEXT + value: $(params.CONTEXT) + - name: DOCKERFILE + value: $(params.DOCKERFILE) + - name: IMAGE + value: $(params.IMAGE) + - name: TLSVERIFY + value: $(params.TLSVERIFY) + - name: IMAGE_EXPIRES_AFTER + value: $(params.IMAGE_EXPIRES_AFTER) + - name: YUM_REPOS_D_SRC + value: $(params.YUM_REPOS_D_SRC) + - name: YUM_REPOS_D_FETCHED + value: $(params.YUM_REPOS_D_FETCHED) + - name: YUM_REPOS_D_TARGET + value: $(params.YUM_REPOS_D_TARGET) + - name: TARGET_STAGE + value: $(params.TARGET_STAGE) + - name: PARAM_BUILDER_IMAGE + value: $(params.BUILDER_IMAGE) + - name: ENTITLEMENT_SECRET + value: $(params.ENTITLEMENT_SECRET) + - name: ADDITIONAL_SECRET + value: $(params.ADDITIONAL_SECRET) + - name: BUILD_ARGS_FILE + value: $(params.BUILD_ARGS_FILE) + - name: ADD_CAPABILITIES + value: $(params.ADD_CAPABILITIES) + - name: SQUASH + value: $(params.SQUASH) + - name: SKIP_UNUSED_STAGES + value: $(params.SKIP_UNUSED_STAGES) + volumeMounts: + - mountPath: /shared + name: shared + steps: + - args: + - $(params.BUILD_ARGS[*]) + computeResources: + limits: + memory: 8Gi + requests: + cpu: 250m + memory: 6Gi + env: + - name: COMMIT_SHA + value: $(params.COMMIT_SHA) + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build + script: | + if [ -n "${PARAM_BUILDER_IMAGE}" ]; then + echo "WARNING: provided deprecated BUILDER_IMAGE parameter has no effect." + fi + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + SOURCE_CODE_DIR=source + if [ -e "$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" + elif [ -e "$SOURCE_CODE_DIR/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$DOCKERFILE" + elif echo "$DOCKERFILE" | grep -q "^https\?://"; then + echo "Fetch Dockerfile from $DOCKERFILE" + dockerfile_path=$(mktemp --suffix=-Dockerfile) + http_code=$(curl -s -L -w "%{http_code}" --output "$dockerfile_path" "$DOCKERFILE") + if [ $http_code != 200 ]; then + echo "No Dockerfile is fetched. Server responds $http_code" + exit 1 + fi + http_code=$(curl -s -L -w "%{http_code}" --output "$dockerfile_path.dockerignore.tmp" "$DOCKERFILE.dockerignore") + if [ $http_code = 200 ]; then + echo "Fetched .dockerignore from $DOCKERFILE.dockerignore" + mv "$dockerfile_path.dockerignore.tmp" $SOURCE_CODE_DIR/$CONTEXT/.dockerignore + fi + else + echo "Cannot find Dockerfile $DOCKERFILE" + exit 1 + fi + if [ -n "$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR" ] && grep -q '^\s*RUN \(./\)\?mvn' "$dockerfile_path"; then + sed -i -e "s|^\s*RUN \(\(./\)\?mvn\)\(.*\)|RUN echo \"mirror.defaulthttp://$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR/v1/cache/default/0/*\" > /tmp/settings.yaml; \1 -s /tmp/settings.yaml \3|g" "$dockerfile_path" + touch /var/lib/containers/java + fi + + # Fixing group permission on /var/lib/containers + chown root:root /var/lib/containers + + sed -i 's/^\s*short-name-mode\s*=\s*.*/short-name-mode = "disabled"/' /etc/containers/registries.conf + + # Setting new namespace to run buildah - 2^32-2 + echo 'root:1:4294967294' | tee -a /etc/subuid >> /etc/subgid + + BUILDAH_ARGS=() + + BASE_IMAGES=$(grep -i '^\s*FROM' "$dockerfile_path" | sed 's/--platform=\S*//' | awk '{print $2}' | (grep -v ^oci-archive: || true)) + if [ "${HERMETIC}" == "true" ]; then + BUILDAH_ARGS+=("--pull=never") + UNSHARE_ARGS="--net" + for image in $BASE_IMAGES; do + if [ "${image}" != "scratch" ]; then + unshare -Ufp --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -- buildah pull $image + fi + done + echo "Build will be executed with network isolation" + fi + + if [ -n "${TARGET_STAGE}" ]; then + BUILDAH_ARGS+=("--target=${TARGET_STAGE}") + fi + + if [ -n "${BUILD_ARGS_FILE}" ]; then + BUILDAH_ARGS+=("--build-arg-file=$(pwd)/$SOURCE_CODE_DIR/${BUILD_ARGS_FILE}") + fi + + for build_arg in "$@"; do + BUILDAH_ARGS+=("--build-arg=$build_arg") + done + + if [ -n "${ADD_CAPABILITIES}" ]; then + BUILDAH_ARGS+=("--cap-add=${ADD_CAPABILITIES}") + fi + + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + if [ "${SKIP_UNUSED_STAGES}" != "true" ] ; then + BUILDAH_ARGS+=("--skip-unused-stages=false") + fi + + if [ -f "$(workspaces.source.path)/cachi2/cachi2.env" ]; then + cp -r "$(workspaces.source.path)/cachi2" /tmp/ + chmod -R go+rwX /tmp/cachi2 + VOLUME_MOUNTS="--volume /tmp/cachi2:/cachi2" + # Read in the whole file (https://unix.stackexchange.com/questions/533277), then + # for each RUN ... line insert the cachi2.env command *after* any options like --mount + sed -E -i \ + -e 'H;1h;$!d;x' \ + -e 's@^\s*(run((\s|\\\n)+-\S+)*(\s|\\\n)+)@\1. /cachi2/cachi2.env \&\& \\\n @igM' \ + "$dockerfile_path" + echo "Prefetched content will be made available" + + prefetched_repo_for_my_arch="/tmp/cachi2/output/deps/rpm/$(uname -m)/repos.d/cachi2.repo" + if [ -f "$prefetched_repo_for_my_arch" ]; then + echo "Adding $prefetched_repo_for_my_arch to $YUM_REPOS_D_FETCHED" + mkdir -p "$YUM_REPOS_D_FETCHED" + cp --no-clobber "$prefetched_repo_for_my_arch" "$YUM_REPOS_D_FETCHED" + fi + fi + + # if yum repofiles stored in git, copy them to mount point outside the source dir + if [ -d "${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}" ]; then + mkdir -p ${YUM_REPOS_D_FETCHED} + cp -r ${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}/* ${YUM_REPOS_D_FETCHED} + fi + + # if anything in the repofiles mount point (either fetched or from git), mount it + if [ -d "${YUM_REPOS_D_FETCHED}" ]; then + chmod -R go+rwX ${YUM_REPOS_D_FETCHED} + mount_point=$(realpath ${YUM_REPOS_D_FETCHED}) + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume ${mount_point}:${YUM_REPOS_D_TARGET}" + fi + + LABELS=( + "--label" "build-date=$(date -u +'%Y-%m-%dT%H:%M:%S')" + "--label" "architecture=$(uname -m)" + "--label" "vcs-type=git" + ) + [ -n "$COMMIT_SHA" ] && LABELS+=("--label" "vcs-ref=$COMMIT_SHA") + [ -n "$IMAGE_EXPIRES_AFTER" ] && LABELS+=("--label" "quay.expires-after=$IMAGE_EXPIRES_AFTER") + + ACTIVATION_KEY_PATH="/activation-key" + ENTITLEMENT_PATH="/entitlement" + + if [ -e /activation-key/org ]; then + cp -r --preserve=mode "$ACTIVATION_KEY_PATH" /tmp/activation-key + mkdir /shared/rhsm-tmp + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume /tmp/activation-key:/activation-key -v /shared/rhsm-tmp:/etc/pki/entitlement:Z" + echo "Adding activation key to the build" + + elif find /entitlement -name "*.pem" >> null; then + cp -r --preserve=mode "$ENTITLEMENT_PATH" /tmp/entitlement + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume /tmp/entitlement:/etc/pki/entitlement" + echo "Adding the entitlement to the build" + fi + + ADDITIONAL_SECRET_PATH="/additional-secret" + ADDITIONAL_SECRET_TMP="/tmp/additional-secret" + if [ -d "$ADDITIONAL_SECRET_PATH" ]; then + cp -r --preserve=mode -L "$ADDITIONAL_SECRET_PATH" $ADDITIONAL_SECRET_TMP + while read -r filename; do + echo "Adding the secret ${ADDITIONAL_SECRET}/${filename} to the build, available at /run/secrets/${ADDITIONAL_SECRET}/${filename}" + BUILDAH_ARGS+=("--secret=id=${ADDITIONAL_SECRET}/${filename},src=$ADDITIONAL_SECRET_TMP/${filename}") + done < <(find $ADDITIONAL_SECRET_TMP -maxdepth 1 -type f -exec basename {} \;) + fi + + unshare -Uf $UNSHARE_ARGS --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -w ${SOURCE_CODE_DIR}/$CONTEXT -- buildah build \ + $VOLUME_MOUNTS \ + "${BUILDAH_ARGS[@]}" \ + "${LABELS[@]}" \ + --tls-verify=$TLSVERIFY --no-cache \ + --ulimit nofile=4096:4096 \ + -f "$dockerfile_path" -t $IMAGE . + + container=$(buildah from --pull-never $IMAGE) + buildah mount $container | tee /shared/container_path + # delete symlinks - they may point outside the container rootfs, messing with SBOM scanners + find $(cat /shared/container_path) -xtype l -delete + echo $container > /shared/container_name + + # Save the SBOM produced by Cachi2 so it can be merged into the final SBOM later + if [ -f "/tmp/cachi2/output/bom.json" ]; then + cp /tmp/cachi2/output/bom.json ./sbom-cachi2.json + fi + + # Expose base image digests + touch $(results.BASE_IMAGES_DIGESTS.path) + for image in $BASE_IMAGES; do + if [ "${image}" != "scratch" ]; then + buildah images --format '{{ .Name }}:{{ .Tag }}@{{ .Digest }}' --filter reference="$image" >> $(results.BASE_IMAGES_DIGESTS.path) + fi + done + + # Needed to generate base images SBOM + echo "$BASE_IMAGES" > $(workspaces.source.path)/base_images_from_dockerfile + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /entitlement + name: etc-pki-entitlement + - mountPath: /activation-key + name: activation-key + - mountPath: /additional-secret + name: additional-secret + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate + script: | + echo "Running syft on the source directory" + syft dir:$(workspaces.source.path)/source --output cyclonedx-json=$(workspaces.source.path)/sbom-source.json + echo "Running syft on the image filesystem" + syft dir:$(cat /shared/container_path) --output cyclonedx-json=$(workspaces.source.path)/sbom-image.json + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + workingDir: $(workspaces.source.path)/source + - image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + name: analyse-dependencies-java-sbom + script: | + if [ -f /var/lib/containers/java ]; then + /opt/jboss/container/java/run/run-java.sh analyse-dependencies path $(cat /shared/container_path) -s $(workspaces.source.path)/sbom-image.json --task-run-name $(context.taskRun.name) --publishers $(results.SBOM_JAVA_COMPONENTS_COUNT.path) + sed -i 's/^/ /' $(results.SBOM_JAVA_COMPONENTS_COUNT.path) # Workaround for SRVKP-2875 + else + touch $(results.JAVA_COMMUNITY_DEPENDENCIES.path) + fi + securityContext: + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + name: merge-syft-sboms + script: | + #!/bin/python3 + import json + + # load SBOMs + with open("./sbom-image.json") as f: + image_sbom = json.load(f) + + with open("./sbom-source.json") as f: + source_sbom = json.load(f) + + # fetch unique components from available SBOMs + def get_identifier(component): + return component["name"] + '@' + component.get("version", "") + + image_sbom_components = image_sbom.setdefault("components", []) + existing_components = [get_identifier(component) for component in image_sbom_components] + + source_sbom_components = source_sbom.get("components", []) + for component in source_sbom_components: + if get_identifier(component) not in existing_components: + image_sbom_components.append(component) + existing_components.append(get_identifier(component)) + + image_sbom_components.sort(key=lambda c: get_identifier(c)) + + # write the CycloneDX unified SBOM + with open("./sbom-cyclonedx.json", "w") as f: + json.dump(image_sbom, f, indent=4) + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/cachi2:0.17.0@sha256:963870f04aeb4a207e79b8eacb47e16c8faa7451f52e92959e0e16cdbd258fb3 + name: merge-cachi2-sbom + script: | + if [ -f "sbom-cachi2.json" ]; then + echo "Merging contents of sbom-cachi2.json into sbom-cyclonedx.json" + merge_syft_sbom sbom-cachi2.json sbom-cyclonedx.json > sbom-temp.json + mv sbom-temp.json sbom-cyclonedx.json + else + echo "Skipping step since no Cachi2 SBOM was produced" + fi + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + name: create-purl-sbom + script: | + #!/bin/python3 + import json + + with open("./sbom-cyclonedx.json") as f: + cyclonedx_sbom = json.load(f) + + purls = [{"purl": component["purl"]} for component in cyclonedx_sbom.get("components", []) if "purl" in component] + purl_content = {"image_contents": {"dependencies": purls}} + + with open("sbom-purl.json", "w") as output_file: + json.dump(purl_content, output_file, indent=4) + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - env: + - name: BASE_IMAGES_DIGESTS_PATH + value: $(results.BASE_IMAGES_DIGESTS.path) + image: quay.io/redhat-appstudio/base-images-sbom-script@sha256:667669e3def018f9dbb8eaf8868887a40bc07842221e9a98f6787edcff021840 + name: create-base-images-sbom + script: | + python3 /app/base_images_sbom_script.py --sbom=sbom-cyclonedx.json --base-images-from-dockerfile=base_images_from_dockerfile --base-images-digests=$BASE_IMAGES_DIGESTS_PATH + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - computeResources: {} + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: inject-sbom-and-push + script: | + if [ -n "${PARAM_BUILDER_IMAGE}" ]; then + echo "WARNING: provided deprecated BUILDER_IMAGE parameter has no effect." + fi + + base_image_name=$(buildah inspect --format '{{ index .ImageAnnotations "org.opencontainers.image.base.name"}}' $IMAGE | cut -f1 -d'@') + base_image_digest=$(buildah inspect --format '{{ index .ImageAnnotations "org.opencontainers.image.base.digest"}}' $IMAGE) + container=$(buildah from --pull-never $IMAGE) + buildah copy $container sbom-cyclonedx.json sbom-purl.json /root/buildinfo/content_manifests/ + buildah config -a org.opencontainers.image.base.name=${base_image_name} -a org.opencontainers.image.base.digest=${base_image_digest} $container + + BUILDAH_ARGS=() + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + buildah commit "${BUILDAH_ARGS[@]}" $container $IMAGE + + status=-1 + max_run=5 + sleep_sec=10 + for run in $(seq 1 $max_run); do + status=0 + [ "$run" -gt 1 ] && sleep $sleep_sec + echo "Pushing sbom image to registry" + buildah push \ + --tls-verify=$TLSVERIFY \ + --digestfile $(workspaces.source.path)/image-digest $IMAGE \ + docker://$IMAGE && break || status=$? + done + if [ "$status" -ne 0 ]; then + echo "Failed to push sbom image to registry after ${max_run} tries" + exit 1 + fi + + cat "$(workspaces.source.path)"/image-digest | tee $(results.IMAGE_DIGEST.path) + echo -n "$IMAGE" | tee $(results.IMAGE_URL.path) + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + workingDir: $(workspaces.source.path) + - args: + - attach + - sbom + - --sbom + - sbom-cyclonedx.json + - --type + - cyclonedx + - $(params.IMAGE) + image: quay.io/redhat-appstudio/cosign:v2.1.1@sha256:c883d6f8d39148f2cea71bff4622d196d89df3e510f36c140c097b932f0dd5d5 + name: upload-sbom + workingDir: $(workspaces.source.path) + volumes: + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared + - name: etc-pki-entitlement + secret: + optional: true + secretName: $(params.ENTITLEMENT_SECRET) + - name: activation-key + secret: + optional: true + secretName: $(params.ACTIVATION_KEY) + - name: additional-secret + secret: + optional: true + secretName: $(params.ADDITIONAL_SECRET) + - configMap: + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) + optional: true + name: trusted-ca + workspaces: + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah-8gb/0.2/buildah-8gb.yaml b/task/buildah-8gb/0.2/buildah-8gb.yaml new file mode 100644 index 0000000000..5ff0d3879e --- /dev/null +++ b/task/buildah-8gb/0.2/buildah-8gb.yaml @@ -0,0 +1,742 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2025-01-21T00:00:00Z" + build.appstudio.redhat.com/expiry-message: 'Instead of using this task, switch + to buildah task (you can find latest bundle reference here: https://quay.io/repository/konflux-ci/tekton-catalog/task-buildah) + and configure build step resources as described in the doc: https://konflux-ci.dev/docs/how-tos/configuring/overriding-compute-resources/' + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: 0.2.1 + build.appstudio.redhat.com/build_type: docker + name: buildah-8gb +spec: + description: |- + Buildah task builds source code into a container image and pushes the image into container registry using buildah tool. + In addition it generates a SBOM file, injects the SBOM file into final container image and pushes the SBOM file as separate image using cosign tool. + When [Java dependency rebuild](https://redhat-appstudio.github.io/docs.stonesoup.io/Documentation/main/cli/proc_enabled_java_dependencies.html) is enabled it triggers rebuilds of Java artifacts. + When prefetch-dependencies task was activated it is using its artifacts to run build in hermetic environment. + params: + - description: Reference of the image buildah will produce. + name: IMAGE + type: string + - default: ./Dockerfile + description: Path to the Dockerfile to build. + name: DOCKERFILE + type: string + - default: . + description: Path to the directory to use as context. + name: CONTEXT + type: string + - default: "true" + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) + name: TLSVERIFY + type: string + - default: "false" + description: Determines if build will be executed without network access. + name: HERMETIC + type: string + - default: "" + description: In case it is not empty, the prefetched content should be made available + to the build. + name: PREFETCH_INPUT + type: string + - default: "" + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. + name: IMAGE_EXPIRES_AFTER + type: string + - default: "" + description: The image is built from this commit. + name: COMMIT_SHA + type: string + - default: repos.d + description: Path in the git repository in which yum repository files are stored + name: YUM_REPOS_D_SRC + - default: fetched.repos.d + description: Path in source workspace where dynamically-fetched repos are present + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE + type: string + - default: etc-pki-entitlement + description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET + type: string + - default: activation-key + description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY + type: string + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS + type: array + - default: "" + description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE + type: string + - default: trusted-ca + description: The name of the ConfigMap to read CA bundle data from. + name: caTrustConfigMapName + type: string + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey + type: string + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES + type: string + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH + type: string + - default: vfs + description: Storage driver to configure for buildah + name: STORAGE_DRIVER + type: string + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES + type: string + - default: [] + description: Additional key=value labels that should be applied to the image + name: LABELS + type: array + - default: "false" + description: Whether to enable privileged mode + name: PRIVILEGED_NESTED + type: string + - default: "false" + description: Skip SBOM-related operations. This will likely cause EC policies + to fail if enabled + name: SKIP_SBOM_GENERATION + type: string + results: + - description: Digest of the image just built + name: IMAGE_DIGEST + - description: Image repository and tag where the built image was pushed + name: IMAGE_URL + - description: Image reference of the built image + name: IMAGE_REF + - description: Reference of SBOM blob digest to enable digest-based verification + from provenance + name: SBOM_BLOB_URL + type: string + - description: The counting of Java components by publisher in JSON format + name: SBOM_JAVA_COMPONENTS_COUNT + type: string + - description: The Java dependencies that came from community sources such as Maven + central. + name: JAVA_COMMUNITY_DEPENDENCIES + stepTemplate: + computeResources: + limits: + cpu: "4" + memory: 4Gi + requests: + cpu: "1" + memory: 1Gi + env: + - name: BUILDAH_FORMAT + value: oci + - name: STORAGE_DRIVER + value: $(params.STORAGE_DRIVER) + - name: HERMETIC + value: $(params.HERMETIC) + - name: SOURCE_CODE_DIR + value: source + - name: CONTEXT + value: $(params.CONTEXT) + - name: IMAGE + value: $(params.IMAGE) + - name: TLSVERIFY + value: $(params.TLSVERIFY) + - name: IMAGE_EXPIRES_AFTER + value: $(params.IMAGE_EXPIRES_AFTER) + - name: YUM_REPOS_D_SRC + value: $(params.YUM_REPOS_D_SRC) + - name: YUM_REPOS_D_FETCHED + value: $(params.YUM_REPOS_D_FETCHED) + - name: YUM_REPOS_D_TARGET + value: $(params.YUM_REPOS_D_TARGET) + - name: TARGET_STAGE + value: $(params.TARGET_STAGE) + - name: ENTITLEMENT_SECRET + value: $(params.ENTITLEMENT_SECRET) + - name: ACTIVATION_KEY + value: $(params.ACTIVATION_KEY) + - name: ADDITIONAL_SECRET + value: $(params.ADDITIONAL_SECRET) + - name: BUILD_ARGS_FILE + value: $(params.BUILD_ARGS_FILE) + - name: ADD_CAPABILITIES + value: $(params.ADD_CAPABILITIES) + - name: SQUASH + value: $(params.SQUASH) + - name: SKIP_UNUSED_STAGES + value: $(params.SKIP_UNUSED_STAGES) + - name: PRIVILEGED_NESTED + value: $(params.PRIVILEGED_NESTED) + - name: SKIP_SBOM_GENERATION + value: $(params.SKIP_SBOM_GENERATION) + volumeMounts: + - mountPath: /shared + name: shared + steps: + - args: + - --build-args + - $(params.BUILD_ARGS[*]) + - --labels + - $(params.LABELS[*]) + computeResources: + limits: + cpu: "4" + memory: 8Gi + requests: + cpu: "1" + memory: 6Gi + env: + - name: COMMIT_SHA + value: $(params.COMMIT_SHA) + - name: DOCKERFILE + value: $(params.DOCKERFILE) + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build + script: | + #!/bin/bash + set -euo pipefail + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + if [ -e "$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" + elif [ -e "$SOURCE_CODE_DIR/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$DOCKERFILE" + elif [ -e "$DOCKERFILE" ]; then + # Instrumented builds (SAST) use this custom dockerffile step as their base + dockerfile_path="$DOCKERFILE" + elif echo "$DOCKERFILE" | grep -q "^https\?://"; then + echo "Fetch Dockerfile from $DOCKERFILE" + dockerfile_path=$(mktemp --suffix=-Dockerfile) + http_code=$(curl -s -S -L -w "%{http_code}" --output "$dockerfile_path" "$DOCKERFILE") + if [ $http_code != 200 ]; then + echo "No Dockerfile is fetched. Server responds $http_code" + exit 1 + fi + http_code=$(curl -s -S -L -w "%{http_code}" --output "$dockerfile_path.dockerignore.tmp" "$DOCKERFILE.dockerignore") + if [ $http_code = 200 ]; then + echo "Fetched .dockerignore from $DOCKERFILE.dockerignore" + mv "$dockerfile_path.dockerignore.tmp" $SOURCE_CODE_DIR/$CONTEXT/.dockerignore + fi + else + echo "Cannot find Dockerfile $DOCKERFILE" + exit 1 + fi + + dockerfile_copy=$(mktemp --tmpdir "$(basename "$dockerfile_path").XXXXXX") + cp "$dockerfile_path" "$dockerfile_copy" + + if [ -n "${JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR-}" ] && grep -q '^\s*RUN \(./\)\?mvn' "$dockerfile_copy"; then + sed -i -e "s|^\s*RUN \(\(./\)\?mvn\)\(.*\)|RUN echo \"mirror.defaulthttp://$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR/v1/cache/default/0/*\" > /tmp/settings.yaml; \1 -s /tmp/settings.yaml \3|g" "$dockerfile_copy" + touch /var/lib/containers/java + fi + + # Fixing group permission on /var/lib/containers + chown root:root /var/lib/containers + + sed -i 's/^\s*short-name-mode\s*=\s*.*/short-name-mode = "disabled"/' /etc/containers/registries.conf + + # Setting new namespace to run buildah - 2^32-2 + echo 'root:1:4294967294' | tee -a /etc/subuid >> /etc/subgid + + build_args=() + if [ -n "${BUILD_ARGS_FILE}" ]; then + # Parse BUILD_ARGS_FILE ourselves because dockerfile-json doesn't support it + echo "Parsing ARGs from $BUILD_ARGS_FILE" + mapfile -t build_args < <( + # https://www.mankier.com/1/buildah-build#--build-arg-file + # delete lines that start with # + # delete blank lines + sed -e '/^#/d' -e '/^\s*$/d' "${SOURCE_CODE_DIR}/${BUILD_ARGS_FILE}" + ) + fi + + LABELS=() + # Split `args` into two sets of arguments. + while [[ $# -gt 0 ]]; do + case $1 in + --build-args) + shift + # Note: this may result in multiple --build-arg=KEY=value flags with the same KEY being + # passed to buildah. In that case, the *last* occurrence takes precedence. This is why + # we append BUILD_ARGS after the content of the BUILD_ARGS_FILE + while [[ $# -gt 0 && $1 != --* ]]; do build_args+=("$1"); shift; done + ;; + --labels) + shift + while [[ $# -gt 0 && $1 != --* ]]; do LABELS+=("--label" "$1"); shift; done + ;; + *) + echo "unexpected argument: $1" >&2 + exit 2 + ;; + esac + done + + BUILD_ARG_FLAGS=() + for build_arg in "${build_args[@]}"; do + BUILD_ARG_FLAGS+=("--build-arg=$build_arg") + done + + dockerfile-json "${BUILD_ARG_FLAGS[@]}" "$dockerfile_copy" > /shared/parsed_dockerfile.json + BASE_IMAGES=$( + jq -r '.Stages[] | select(.From | .Stage or .Scratch | not) | .BaseName | select(test("^oci-archive:") | not)' /shared/parsed_dockerfile.json + ) + + BUILDAH_ARGS=() + UNSHARE_ARGS=() + + if [ "${HERMETIC}" == "true" ]; then + BUILDAH_ARGS+=("--pull=never") + UNSHARE_ARGS+=("--net") + + for image in $BASE_IMAGES; do + unshare -Ufp --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -- buildah pull $image + done + echo "Build will be executed with network isolation" + fi + + if [ -n "${TARGET_STAGE}" ]; then + BUILDAH_ARGS+=("--target=${TARGET_STAGE}") + fi + + BUILDAH_ARGS+=("${BUILD_ARG_FLAGS[@]}") + + if [ "${PRIVILEGED_NESTED}" == "true" ]; then + BUILDAH_ARGS+=("--security-opt=label=disable") + BUILDAH_ARGS+=("--cap-add=all") + BUILDAH_ARGS+=("--device=/dev/fuse") + fi + + if [ -n "${ADD_CAPABILITIES}" ]; then + BUILDAH_ARGS+=("--cap-add=${ADD_CAPABILITIES}") + fi + + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + if [ "${SKIP_UNUSED_STAGES}" != "true" ] ; then + BUILDAH_ARGS+=("--skip-unused-stages=false") + fi + + VOLUME_MOUNTS=() + + if [ -f "$(workspaces.source.path)/cachi2/cachi2.env" ]; then + cp -r "$(workspaces.source.path)/cachi2" /tmp/ + chmod -R go+rwX /tmp/cachi2 + VOLUME_MOUNTS+=(--volume /tmp/cachi2:/cachi2) + # Read in the whole file (https://unix.stackexchange.com/questions/533277), then + # for each RUN ... line insert the cachi2.env command *after* any options like --mount + sed -E -i \ + -e 'H;1h;$!d;x' \ + -e 's@^\s*(run((\s|\\\n)+-\S+)*(\s|\\\n)+)@\1. /cachi2/cachi2.env \&\& \\\n @igM' \ + "$dockerfile_copy" + echo "Prefetched content will be made available" + + prefetched_repo_for_my_arch="/tmp/cachi2/output/deps/rpm/$(uname -m)/repos.d/cachi2.repo" + if [ -f "$prefetched_repo_for_my_arch" ]; then + echo "Adding $prefetched_repo_for_my_arch to $YUM_REPOS_D_FETCHED" + mkdir -p "$YUM_REPOS_D_FETCHED" + cp --no-clobber "$prefetched_repo_for_my_arch" "$YUM_REPOS_D_FETCHED" + fi + fi + + # if yum repofiles stored in git, copy them to mount point outside the source dir + if [ -d "${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}" ]; then + mkdir -p ${YUM_REPOS_D_FETCHED} + cp -r ${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}/* ${YUM_REPOS_D_FETCHED} + fi + + # if anything in the repofiles mount point (either fetched or from git), mount it + if [ -d "${YUM_REPOS_D_FETCHED}" ]; then + chmod -R go+rwX ${YUM_REPOS_D_FETCHED} + mount_point=$(realpath ${YUM_REPOS_D_FETCHED}) + VOLUME_MOUNTS+=(--volume "${mount_point}:${YUM_REPOS_D_TARGET}") + fi + + DEFAULT_LABELS=( + "--label" "build-date=$(date -u +'%Y-%m-%dT%H:%M:%S')" + "--label" "architecture=$(uname -m)" + "--label" "vcs-type=git" + ) + [ -n "$COMMIT_SHA" ] && DEFAULT_LABELS+=("--label" "vcs-ref=$COMMIT_SHA") + [ -n "$IMAGE_EXPIRES_AFTER" ] && DEFAULT_LABELS+=("--label" "quay.expires-after=$IMAGE_EXPIRES_AFTER") + + # Concatenate defaults and explicit labels. If a label appears twice, the last one wins. + LABELS=("${DEFAULT_LABELS[@]}" "${LABELS[@]}") + + ACTIVATION_KEY_PATH="/activation-key" + ENTITLEMENT_PATH="/entitlement" + + # 0. if hermetic=true, skip all subscription related stuff + # 1. do not enable activation key and entitlement at same time. If both vars are provided, prefer activation key. + # 2. Activation-keys will be used when the key 'org' exists in the activation key secret. + # 3. try to pre-register and mount files to the correct location so that users do no need to modify Dockerfiles. + # 3. If the Dockerfile contains the string "subcription-manager register", add the activation-keys volume + # to buildah but don't pre-register for backwards compatibility. Mount an empty directory on + # shared emptydir volume to "/etc/pki/entitlement" to prevent certificates from being included + + if [ "${HERMETIC}" != "true" ] && [ -e /activation-key/org ]; then + cp -r --preserve=mode "$ACTIVATION_KEY_PATH" /tmp/activation-key + mkdir -p /shared/rhsm/etc/pki/entitlement + mkdir -p /shared/rhsm/etc/pki/consumer + + VOLUME_MOUNTS+=(-v /tmp/activation-key:/activation-key \ + -v /shared/rhsm/etc/pki/entitlement:/etc/pki/entitlement:Z \ + -v /shared/rhsm/etc/pki/consumer:/etc/pki/consumer:Z) + echo "Adding activation key to the build" + + if ! grep -E "^[^#]*subscription-manager.[^#]*register" "$dockerfile_path"; then + # user is not running registration in the Containerfile: pre-register. + echo "Pre-registering with subscription manager." + subscription-manager register --org "$(cat /tmp/activation-key/org)" --activationkey "$(cat /tmp/activation-key/activationkey)" + trap 'subscription-manager unregister || true' EXIT + + # copy generated certificates to /shared volume + cp /etc/pki/entitlement/*.pem /shared/rhsm/etc/pki/entitlement + cp /etc/pki/consumer/*.pem /shared/rhsm/etc/pki/consumer + + # and then mount get /etc/rhsm/ca/redhat-uep.pem into /run/secrets/rhsm/ca + VOLUME_MOUNTS+=(--volume /etc/rhsm/ca/redhat-uep.pem:/etc/rhsm/ca/redhat-uep.pem:Z) + fi + + elif [ "${HERMETIC}" != "true" ] && find /entitlement -name "*.pem" >> null; then + cp -r --preserve=mode "$ENTITLEMENT_PATH" /tmp/entitlement + VOLUME_MOUNTS+=(--volume /tmp/entitlement:/etc/pki/entitlement) + echo "Adding the entitlement to the build" + fi + + if [ -n "${ADDITIONAL_VOLUME_MOUNTS-}" ]; then + # ADDITIONAL_VOLUME_MOUNTS allows to specify more volumes for the build. + # Instrumented builds (SAST) use this step as their base and add some other tools. + while read -r volume_mount; do + VOLUME_MOUNTS+=("--volume=$volume_mount") + done <<< "$ADDITIONAL_VOLUME_MOUNTS" + fi + + ADDITIONAL_SECRET_PATH="/additional-secret" + ADDITIONAL_SECRET_TMP="/tmp/additional-secret" + if [ -d "$ADDITIONAL_SECRET_PATH" ]; then + cp -r --preserve=mode -L "$ADDITIONAL_SECRET_PATH" $ADDITIONAL_SECRET_TMP + while read -r filename; do + echo "Adding the secret ${ADDITIONAL_SECRET}/${filename} to the build, available at /run/secrets/${ADDITIONAL_SECRET}/${filename}" + BUILDAH_ARGS+=("--secret=id=${ADDITIONAL_SECRET}/${filename},src=$ADDITIONAL_SECRET_TMP/${filename}") + done < <(find $ADDITIONAL_SECRET_TMP -maxdepth 1 -type f -exec basename {} \;) + fi + + # Prevent ShellCheck from giving a warning because 'image' is defined and 'IMAGE' is not. + declare IMAGE + + buildah_cmd_array=( + buildah build + "${VOLUME_MOUNTS[@]}" + "${BUILDAH_ARGS[@]}" + "${LABELS[@]}" + --tls-verify="$TLSVERIFY" --no-cache + --ulimit nofile=4096:4096 + -f "$dockerfile_copy" -t "$IMAGE" . + ) + buildah_cmd=$(printf "%q " "${buildah_cmd_array[@]}") + + if [ "${HERMETIC}" == "true" ]; then + # enabling loopback adapter enables Bazel builds to work in hermetic mode. + command="ip link set lo up && $buildah_cmd" + else + command="$buildah_cmd" + fi + + # disable host subcription manager integration + find /usr/share/rhel/secrets -type l -exec unlink {} \; + + unshare -Uf "${UNSHARE_ARGS[@]}" --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -w "${SOURCE_CODE_DIR}/$CONTEXT" -- sh -c "$command" + + container=$(buildah from --pull-never "$IMAGE") + + # Save the SBOM produced by Cachi2 so it can be merged into the final SBOM later + if [ -f "/tmp/cachi2/output/bom.json" ]; then + echo "Making copy of sbom-cachi2.json" + cp /tmp/cachi2/output/bom.json ./sbom-cachi2.json + fi + + buildah mount $container | tee /shared/container_path + # delete symlinks - they may point outside the container rootfs, messing with SBOM scanners + find $(cat /shared/container_path) -xtype l -delete + echo $container > /shared/container_name + + touch /shared/base_images_digests + echo "Recording base image digests used" + for image in $BASE_IMAGES; do + base_image_digest=$(buildah images --format '{{ .Name }}:{{ .Tag }}@{{ .Digest }}' --filter reference="$image") + # In some cases, there might be BASE_IMAGES, but not any associated digest. This happens + # if buildah did not use that particular image during build because it was skipped + if [ -n "$base_image_digest" ]; then + echo "$image $base_image_digest" | tee -a /shared/base_images_digests + fi + done + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /entitlement + name: etc-pki-entitlement + - mountPath: /activation-key + name: activation-key + - mountPath: /additional-secret + name: additional-secret + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/icm-injection-scripts:latest@sha256:523689a26c74790be67d6036820abfb9aa10bec6efff98cc1c9b74bd2f99eeb1 + name: icm + script: | + #!/bin/bash + set -euo pipefail + /scripts/inject-icm.sh "$IMAGE" + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: push + script: | + #!/bin/bash + set -e + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + retries=5 + # Push to a unique tag based on the TaskRun name to avoid race conditions + echo "Pushing to ${IMAGE%:*}:${TASKRUN_NAME}" + if ! buildah push \ + --retry "$retries" \ + --tls-verify="$TLSVERIFY" \ + "$IMAGE" \ + "docker://${IMAGE%:*}:$(context.taskRun.name)"; + then + echo "Failed to push sbom image to ${IMAGE%:*}:$(context.taskRun.name) after ${retries} tries" + exit 1 + fi + + # Push to a tag based on the git revision + echo "Pushing to ${IMAGE}" + if ! buildah push \ + --retry "$retries" \ + --tls-verify="$TLSVERIFY" \ + --digestfile "$(workspaces.source.path)/image-digest" "$IMAGE" \ + "docker://$IMAGE"; + then + echo "Failed to push sbom image to $IMAGE after ${retries} tries" + exit 1 + fi + + cat "$(workspaces.source.path)"/image-digest | tee $(results.IMAGE_DIGEST.path) + echo -n "$IMAGE" | tee $(results.IMAGE_URL.path) + { + echo -n "${IMAGE}@" + cat "$(workspaces.source.path)/image-digest" + } > "$(results.IMAGE_REF.path)" + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate + script: | + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + echo "Running syft on the source directory" + syft dir:"$(workspaces.source.path)/$SOURCE_CODE_DIR/$CONTEXT" --output cyclonedx-json@1.5="$(workspaces.source.path)/sbom-source.json" + echo "Running syft on the image filesystem" + syft dir:"$(cat /shared/container_path)" --output cyclonedx-json@1.5="$(workspaces.source.path)/sbom-image.json" + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + workingDir: $(workspaces.source.path)/source + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + name: analyse-dependencies-java-sbom + script: | + if [ -f /var/lib/containers/java ]; then + /opt/jboss/container/java/run/run-java.sh analyse-dependencies path $(cat /shared/container_path) -s $(workspaces.source.path)/sbom-image.json --task-run-name $(context.taskRun.name) --publishers $(results.SBOM_JAVA_COMPONENTS_COUNT.path) + sed -i 's/^/ /' $(results.SBOM_JAVA_COMPONENTS_COUNT.path) # Workaround for SRVKP-2875 + else + touch $(results.JAVA_COMMUNITY_DEPENDENCIES.path) + fi + securityContext: + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/sbom-utility-scripts@sha256:7f4ce55033077f37eda61c67b4289949471c36d23ae0cc66fd4113e615b3ef93 + name: prepare-sboms + script: | + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + echo "Merging contents of sbom-source.json and sbom-image.json into sbom-cyclonedx.json" + python3 /scripts/merge_syft_sboms.py + + if [ -f "sbom-cachi2.json" ]; then + echo "Merging contents of sbom-cachi2.json into sbom-cyclonedx.json" + python3 /scripts/merge_cachi2_sboms.py sbom-cachi2.json sbom-cyclonedx.json > sbom-temp.json + mv sbom-temp.json sbom-cyclonedx.json + fi + + echo "Adding base images data to sbom-cyclonedx.json" + python3 /scripts/base_images_sbom_script.py \ + --sbom=sbom-cyclonedx.json \ + --parsed-dockerfile=/shared/parsed_dockerfile.json \ + --base-images-digests=/shared/base_images_digests + + echo "Adding image reference to sbom" + IMAGE_URL="$(cat "$(results.IMAGE_URL.path)")" + IMAGE_DIGEST="$(cat "$(results.IMAGE_DIGEST.path)")" + + python3 /scripts/add_image_reference.py \ + --image-url "$IMAGE_URL" \ + --image-digest "$IMAGE_DIGEST" \ + --input-file sbom-cyclonedx.json \ + --output-file /tmp/sbom-cyclonedx.tmp.json + + mv /tmp/sbom-cyclonedx.tmp.json sbom-cyclonedx.json + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/appstudio-utils:48c311af02858e2422d6229600e9959e496ddef1@sha256:91ddd999271f65d8ec8487b10f3dd378f81aa894e11b9af4d10639fd52bba7e8 + name: upload-sbom + script: | + #!/bin/bash + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + cosign attach sbom --sbom sbom-cyclonedx.json --type cyclonedx "$(cat "$(results.IMAGE_REF.path)")" + + # Remove tag from IMAGE while allowing registry to contain a port number. + sbom_repo="${IMAGE%:*}" + sbom_digest="$(sha256sum sbom-cyclonedx.json | cut -d' ' -f1)" + # The SBOM_BLOB_URL is created by `cosign attach sbom`. + echo -n "${sbom_repo}@sha256:${sbom_digest}" | tee "$(results.SBOM_BLOB_URL.path)" + volumeMounts: + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + volumes: + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared + - name: etc-pki-entitlement + secret: + optional: true + secretName: $(params.ENTITLEMENT_SECRET) + - name: activation-key + secret: + optional: true + secretName: $(params.ACTIVATION_KEY) + - name: additional-secret + secret: + optional: true + secretName: $(params.ADDITIONAL_SECRET) + - configMap: + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) + optional: true + name: trusted-ca + workspaces: + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah-min/0.1/buildah-min.yaml b/task/buildah-min/0.1/buildah-min.yaml new file mode 100644 index 0000000000..689f3d5e29 --- /dev/null +++ b/task/buildah-min/0.1/buildah-min.yaml @@ -0,0 +1,582 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2024-11-13T00:00:00Z" + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: "0.1" + build.appstudio.redhat.com/build_type: docker + name: buildah-min +spec: + description: |- + Buildah task builds source code into a container image and pushes the image into container registry using buildah tool. + In addition it generates a SBOM file, injects the SBOM file into final container image and pushes the SBOM file as separate image using cosign tool. + When [Java dependency rebuild](https://redhat-appstudio.github.io/docs.stonesoup.io/Documentation/main/cli/proc_enabled_java_dependencies.html) is enabled it triggers rebuilds of Java artifacts. + When prefetch-dependencies task was activated it is using its artifacts to run build in hermetic environment. + params: + - description: Reference of the image buildah will produce. + name: IMAGE + type: string + - default: "" + description: Deprecated. Has no effect. Will be removed in the future. + name: BUILDER_IMAGE + type: string + - default: ./Dockerfile + description: Path to the Dockerfile to build. + name: DOCKERFILE + type: string + - default: . + description: Path to the directory to use as context. + name: CONTEXT + type: string + - default: "true" + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) + name: TLSVERIFY + type: string + - default: "" + description: unused, should be removed in next task version + name: DOCKER_AUTH + type: string + - default: "false" + description: Determines if build will be executed without network access. + name: HERMETIC + type: string + - default: "" + description: In case it is not empty, the prefetched content should be made available + to the build. + name: PREFETCH_INPUT + type: string + - default: "" + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. + name: IMAGE_EXPIRES_AFTER + type: string + - default: "" + description: The image is built from this commit. + name: COMMIT_SHA + type: string + - default: repos.d + description: Path in the git repository in which yum repository files are stored + name: YUM_REPOS_D_SRC + - default: fetched.repos.d + description: Path in source workspace where dynamically-fetched repos are present + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE + type: string + - default: etc-pki-entitlement + description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET + type: string + - default: activation-key + description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY + type: string + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS + type: array + - default: "" + description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE + type: string + - default: trusted-ca + description: The name of the ConfigMap to read CA bundle data from. + name: caTrustConfigMapName + type: string + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey + type: string + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES + type: string + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH + type: string + - default: vfs + description: Storage driver to configure for buildah + name: STORAGE_DRIVER + type: string + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES + type: string + results: + - description: Digest of the image just built + name: IMAGE_DIGEST + - description: Image repository and tag where the built image was pushed + name: IMAGE_URL + - description: Digests of the base images used for build + name: BASE_IMAGES_DIGESTS + - description: The counting of Java components by publisher in JSON format + name: SBOM_JAVA_COMPONENTS_COUNT + type: string + - description: The Java dependencies that came from community sources such as Maven + central. + name: JAVA_COMMUNITY_DEPENDENCIES + stepTemplate: + env: + - name: BUILDAH_FORMAT + value: oci + - name: STORAGE_DRIVER + value: $(params.STORAGE_DRIVER) + - name: HERMETIC + value: $(params.HERMETIC) + - name: CONTEXT + value: $(params.CONTEXT) + - name: DOCKERFILE + value: $(params.DOCKERFILE) + - name: IMAGE + value: $(params.IMAGE) + - name: TLSVERIFY + value: $(params.TLSVERIFY) + - name: IMAGE_EXPIRES_AFTER + value: $(params.IMAGE_EXPIRES_AFTER) + - name: YUM_REPOS_D_SRC + value: $(params.YUM_REPOS_D_SRC) + - name: YUM_REPOS_D_FETCHED + value: $(params.YUM_REPOS_D_FETCHED) + - name: YUM_REPOS_D_TARGET + value: $(params.YUM_REPOS_D_TARGET) + - name: TARGET_STAGE + value: $(params.TARGET_STAGE) + - name: PARAM_BUILDER_IMAGE + value: $(params.BUILDER_IMAGE) + - name: ENTITLEMENT_SECRET + value: $(params.ENTITLEMENT_SECRET) + - name: ADDITIONAL_SECRET + value: $(params.ADDITIONAL_SECRET) + - name: BUILD_ARGS_FILE + value: $(params.BUILD_ARGS_FILE) + - name: ADD_CAPABILITIES + value: $(params.ADD_CAPABILITIES) + - name: SQUASH + value: $(params.SQUASH) + - name: SKIP_UNUSED_STAGES + value: $(params.SKIP_UNUSED_STAGES) + volumeMounts: + - mountPath: /shared + name: shared + steps: + - args: + - $(params.BUILD_ARGS[*]) + computeResources: + limits: + memory: 4Gi + requests: + cpu: 100m + memory: 1Gi + env: + - name: COMMIT_SHA + value: $(params.COMMIT_SHA) + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build + script: | + if [ -n "${PARAM_BUILDER_IMAGE}" ]; then + echo "WARNING: provided deprecated BUILDER_IMAGE parameter has no effect." + fi + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + SOURCE_CODE_DIR=source + if [ -e "$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" + elif [ -e "$SOURCE_CODE_DIR/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$DOCKERFILE" + elif echo "$DOCKERFILE" | grep -q "^https\?://"; then + echo "Fetch Dockerfile from $DOCKERFILE" + dockerfile_path=$(mktemp --suffix=-Dockerfile) + http_code=$(curl -s -L -w "%{http_code}" --output "$dockerfile_path" "$DOCKERFILE") + if [ $http_code != 200 ]; then + echo "No Dockerfile is fetched. Server responds $http_code" + exit 1 + fi + http_code=$(curl -s -L -w "%{http_code}" --output "$dockerfile_path.dockerignore.tmp" "$DOCKERFILE.dockerignore") + if [ $http_code = 200 ]; then + echo "Fetched .dockerignore from $DOCKERFILE.dockerignore" + mv "$dockerfile_path.dockerignore.tmp" $SOURCE_CODE_DIR/$CONTEXT/.dockerignore + fi + else + echo "Cannot find Dockerfile $DOCKERFILE" + exit 1 + fi + if [ -n "$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR" ] && grep -q '^\s*RUN \(./\)\?mvn' "$dockerfile_path"; then + sed -i -e "s|^\s*RUN \(\(./\)\?mvn\)\(.*\)|RUN echo \"mirror.defaulthttp://$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR/v1/cache/default/0/*\" > /tmp/settings.yaml; \1 -s /tmp/settings.yaml \3|g" "$dockerfile_path" + touch /var/lib/containers/java + fi + + # Fixing group permission on /var/lib/containers + chown root:root /var/lib/containers + + sed -i 's/^\s*short-name-mode\s*=\s*.*/short-name-mode = "disabled"/' /etc/containers/registries.conf + + # Setting new namespace to run buildah - 2^32-2 + echo 'root:1:4294967294' | tee -a /etc/subuid >> /etc/subgid + + BUILDAH_ARGS=() + + BASE_IMAGES=$(grep -i '^\s*FROM' "$dockerfile_path" | sed 's/--platform=\S*//' | awk '{print $2}' | (grep -v ^oci-archive: || true)) + if [ "${HERMETIC}" == "true" ]; then + BUILDAH_ARGS+=("--pull=never") + UNSHARE_ARGS="--net" + for image in $BASE_IMAGES; do + if [ "${image}" != "scratch" ]; then + unshare -Ufp --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -- buildah pull $image + fi + done + echo "Build will be executed with network isolation" + fi + + if [ -n "${TARGET_STAGE}" ]; then + BUILDAH_ARGS+=("--target=${TARGET_STAGE}") + fi + + if [ -n "${BUILD_ARGS_FILE}" ]; then + BUILDAH_ARGS+=("--build-arg-file=$(pwd)/$SOURCE_CODE_DIR/${BUILD_ARGS_FILE}") + fi + + for build_arg in "$@"; do + BUILDAH_ARGS+=("--build-arg=$build_arg") + done + + if [ -n "${ADD_CAPABILITIES}" ]; then + BUILDAH_ARGS+=("--cap-add=${ADD_CAPABILITIES}") + fi + + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + if [ "${SKIP_UNUSED_STAGES}" != "true" ] ; then + BUILDAH_ARGS+=("--skip-unused-stages=false") + fi + + if [ -f "$(workspaces.source.path)/cachi2/cachi2.env" ]; then + cp -r "$(workspaces.source.path)/cachi2" /tmp/ + chmod -R go+rwX /tmp/cachi2 + VOLUME_MOUNTS="--volume /tmp/cachi2:/cachi2" + # Read in the whole file (https://unix.stackexchange.com/questions/533277), then + # for each RUN ... line insert the cachi2.env command *after* any options like --mount + sed -E -i \ + -e 'H;1h;$!d;x' \ + -e 's@^\s*(run((\s|\\\n)+-\S+)*(\s|\\\n)+)@\1. /cachi2/cachi2.env \&\& \\\n @igM' \ + "$dockerfile_path" + echo "Prefetched content will be made available" + + prefetched_repo_for_my_arch="/tmp/cachi2/output/deps/rpm/$(uname -m)/repos.d/cachi2.repo" + if [ -f "$prefetched_repo_for_my_arch" ]; then + echo "Adding $prefetched_repo_for_my_arch to $YUM_REPOS_D_FETCHED" + mkdir -p "$YUM_REPOS_D_FETCHED" + cp --no-clobber "$prefetched_repo_for_my_arch" "$YUM_REPOS_D_FETCHED" + fi + fi + + # if yum repofiles stored in git, copy them to mount point outside the source dir + if [ -d "${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}" ]; then + mkdir -p ${YUM_REPOS_D_FETCHED} + cp -r ${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}/* ${YUM_REPOS_D_FETCHED} + fi + + # if anything in the repofiles mount point (either fetched or from git), mount it + if [ -d "${YUM_REPOS_D_FETCHED}" ]; then + chmod -R go+rwX ${YUM_REPOS_D_FETCHED} + mount_point=$(realpath ${YUM_REPOS_D_FETCHED}) + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume ${mount_point}:${YUM_REPOS_D_TARGET}" + fi + + LABELS=( + "--label" "build-date=$(date -u +'%Y-%m-%dT%H:%M:%S')" + "--label" "architecture=$(uname -m)" + "--label" "vcs-type=git" + ) + [ -n "$COMMIT_SHA" ] && LABELS+=("--label" "vcs-ref=$COMMIT_SHA") + [ -n "$IMAGE_EXPIRES_AFTER" ] && LABELS+=("--label" "quay.expires-after=$IMAGE_EXPIRES_AFTER") + + ACTIVATION_KEY_PATH="/activation-key" + ENTITLEMENT_PATH="/entitlement" + + if [ -e /activation-key/org ]; then + cp -r --preserve=mode "$ACTIVATION_KEY_PATH" /tmp/activation-key + mkdir /shared/rhsm-tmp + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume /tmp/activation-key:/activation-key -v /shared/rhsm-tmp:/etc/pki/entitlement:Z" + echo "Adding activation key to the build" + + elif find /entitlement -name "*.pem" >> null; then + cp -r --preserve=mode "$ENTITLEMENT_PATH" /tmp/entitlement + VOLUME_MOUNTS="${VOLUME_MOUNTS} --volume /tmp/entitlement:/etc/pki/entitlement" + echo "Adding the entitlement to the build" + fi + + ADDITIONAL_SECRET_PATH="/additional-secret" + ADDITIONAL_SECRET_TMP="/tmp/additional-secret" + if [ -d "$ADDITIONAL_SECRET_PATH" ]; then + cp -r --preserve=mode -L "$ADDITIONAL_SECRET_PATH" $ADDITIONAL_SECRET_TMP + while read -r filename; do + echo "Adding the secret ${ADDITIONAL_SECRET}/${filename} to the build, available at /run/secrets/${ADDITIONAL_SECRET}/${filename}" + BUILDAH_ARGS+=("--secret=id=${ADDITIONAL_SECRET}/${filename},src=$ADDITIONAL_SECRET_TMP/${filename}") + done < <(find $ADDITIONAL_SECRET_TMP -maxdepth 1 -type f -exec basename {} \;) + fi + + unshare -Uf $UNSHARE_ARGS --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -w ${SOURCE_CODE_DIR}/$CONTEXT -- buildah build \ + $VOLUME_MOUNTS \ + "${BUILDAH_ARGS[@]}" \ + "${LABELS[@]}" \ + --tls-verify=$TLSVERIFY --no-cache \ + --ulimit nofile=4096:4096 \ + -f "$dockerfile_path" -t $IMAGE . + + container=$(buildah from --pull-never $IMAGE) + buildah mount $container | tee /shared/container_path + # delete symlinks - they may point outside the container rootfs, messing with SBOM scanners + find $(cat /shared/container_path) -xtype l -delete + echo $container > /shared/container_name + + # Save the SBOM produced by Cachi2 so it can be merged into the final SBOM later + if [ -f "/tmp/cachi2/output/bom.json" ]; then + cp /tmp/cachi2/output/bom.json ./sbom-cachi2.json + fi + + # Expose base image digests + touch $(results.BASE_IMAGES_DIGESTS.path) + for image in $BASE_IMAGES; do + if [ "${image}" != "scratch" ]; then + buildah images --format '{{ .Name }}:{{ .Tag }}@{{ .Digest }}' --filter reference="$image" >> $(results.BASE_IMAGES_DIGESTS.path) + fi + done + + # Needed to generate base images SBOM + echo "$BASE_IMAGES" > $(workspaces.source.path)/base_images_from_dockerfile + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /entitlement + name: etc-pki-entitlement + - mountPath: /activation-key + name: activation-key + - mountPath: /additional-secret + name: additional-secret + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate + script: | + echo "Running syft on the source directory" + syft dir:$(workspaces.source.path)/source --output cyclonedx-json=$(workspaces.source.path)/sbom-source.json + echo "Running syft on the image filesystem" + syft dir:$(cat /shared/container_path) --output cyclonedx-json=$(workspaces.source.path)/sbom-image.json + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + workingDir: $(workspaces.source.path)/source + - image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + name: analyse-dependencies-java-sbom + script: | + if [ -f /var/lib/containers/java ]; then + /opt/jboss/container/java/run/run-java.sh analyse-dependencies path $(cat /shared/container_path) -s $(workspaces.source.path)/sbom-image.json --task-run-name $(context.taskRun.name) --publishers $(results.SBOM_JAVA_COMPONENTS_COUNT.path) + sed -i 's/^/ /' $(results.SBOM_JAVA_COMPONENTS_COUNT.path) # Workaround for SRVKP-2875 + else + touch $(results.JAVA_COMMUNITY_DEPENDENCIES.path) + fi + securityContext: + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + name: merge-syft-sboms + script: | + #!/bin/python3 + import json + + # load SBOMs + with open("./sbom-image.json") as f: + image_sbom = json.load(f) + + with open("./sbom-source.json") as f: + source_sbom = json.load(f) + + # fetch unique components from available SBOMs + def get_identifier(component): + return component["name"] + '@' + component.get("version", "") + + image_sbom_components = image_sbom.setdefault("components", []) + existing_components = [get_identifier(component) for component in image_sbom_components] + + source_sbom_components = source_sbom.get("components", []) + for component in source_sbom_components: + if get_identifier(component) not in existing_components: + image_sbom_components.append(component) + existing_components.append(get_identifier(component)) + + image_sbom_components.sort(key=lambda c: get_identifier(c)) + + # write the CycloneDX unified SBOM + with open("./sbom-cyclonedx.json", "w") as f: + json.dump(image_sbom, f, indent=4) + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/cachi2:0.17.0@sha256:963870f04aeb4a207e79b8eacb47e16c8faa7451f52e92959e0e16cdbd258fb3 + name: merge-cachi2-sbom + script: | + if [ -f "sbom-cachi2.json" ]; then + echo "Merging contents of sbom-cachi2.json into sbom-cyclonedx.json" + merge_syft_sbom sbom-cachi2.json sbom-cyclonedx.json > sbom-temp.json + mv sbom-temp.json sbom-cyclonedx.json + else + echo "Skipping step since no Cachi2 SBOM was produced" + fi + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + name: create-purl-sbom + script: | + #!/bin/python3 + import json + + with open("./sbom-cyclonedx.json") as f: + cyclonedx_sbom = json.load(f) + + purls = [{"purl": component["purl"]} for component in cyclonedx_sbom.get("components", []) if "purl" in component] + purl_content = {"image_contents": {"dependencies": purls}} + + with open("sbom-purl.json", "w") as output_file: + json.dump(purl_content, output_file, indent=4) + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - env: + - name: BASE_IMAGES_DIGESTS_PATH + value: $(results.BASE_IMAGES_DIGESTS.path) + image: quay.io/redhat-appstudio/base-images-sbom-script@sha256:667669e3def018f9dbb8eaf8868887a40bc07842221e9a98f6787edcff021840 + name: create-base-images-sbom + script: | + python3 /app/base_images_sbom_script.py --sbom=sbom-cyclonedx.json --base-images-from-dockerfile=base_images_from_dockerfile --base-images-digests=$BASE_IMAGES_DIGESTS_PATH + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - computeResources: {} + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: inject-sbom-and-push + script: | + if [ -n "${PARAM_BUILDER_IMAGE}" ]; then + echo "WARNING: provided deprecated BUILDER_IMAGE parameter has no effect." + fi + + base_image_name=$(buildah inspect --format '{{ index .ImageAnnotations "org.opencontainers.image.base.name"}}' $IMAGE | cut -f1 -d'@') + base_image_digest=$(buildah inspect --format '{{ index .ImageAnnotations "org.opencontainers.image.base.digest"}}' $IMAGE) + container=$(buildah from --pull-never $IMAGE) + buildah copy $container sbom-cyclonedx.json sbom-purl.json /root/buildinfo/content_manifests/ + buildah config -a org.opencontainers.image.base.name=${base_image_name} -a org.opencontainers.image.base.digest=${base_image_digest} $container + + BUILDAH_ARGS=() + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + buildah commit "${BUILDAH_ARGS[@]}" $container $IMAGE + + status=-1 + max_run=5 + sleep_sec=10 + for run in $(seq 1 $max_run); do + status=0 + [ "$run" -gt 1 ] && sleep $sleep_sec + echo "Pushing sbom image to registry" + buildah push \ + --tls-verify=$TLSVERIFY \ + --digestfile $(workspaces.source.path)/image-digest $IMAGE \ + docker://$IMAGE && break || status=$? + done + if [ "$status" -ne 0 ]; then + echo "Failed to push sbom image to registry after ${max_run} tries" + exit 1 + fi + + cat "$(workspaces.source.path)"/image-digest | tee $(results.IMAGE_DIGEST.path) + echo -n "$IMAGE" | tee $(results.IMAGE_URL.path) + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + workingDir: $(workspaces.source.path) + - args: + - attach + - sbom + - --sbom + - sbom-cyclonedx.json + - --type + - cyclonedx + - $(params.IMAGE) + image: quay.io/redhat-appstudio/cosign:v2.1.1@sha256:c883d6f8d39148f2cea71bff4622d196d89df3e510f36c140c097b932f0dd5d5 + name: upload-sbom + workingDir: $(workspaces.source.path) + volumes: + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared + - name: etc-pki-entitlement + secret: + optional: true + secretName: $(params.ENTITLEMENT_SECRET) + - name: activation-key + secret: + optional: true + secretName: $(params.ACTIVATION_KEY) + - name: additional-secret + secret: + optional: true + secretName: $(params.ADDITIONAL_SECRET) + - configMap: + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) + optional: true + name: trusted-ca + workspaces: + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah-min/0.2/buildah-min.yaml b/task/buildah-min/0.2/buildah-min.yaml new file mode 100644 index 0000000000..400f70dfec --- /dev/null +++ b/task/buildah-min/0.2/buildah-min.yaml @@ -0,0 +1,738 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: 0.2.1 + build.appstudio.redhat.com/build_type: docker + name: buildah-min +spec: + description: |- + Buildah task builds source code into a container image and pushes the image into container registry using buildah tool. + In addition it generates a SBOM file, injects the SBOM file into final container image and pushes the SBOM file as separate image using cosign tool. + When [Java dependency rebuild](https://redhat-appstudio.github.io/docs.stonesoup.io/Documentation/main/cli/proc_enabled_java_dependencies.html) is enabled it triggers rebuilds of Java artifacts. + When prefetch-dependencies task was activated it is using its artifacts to run build in hermetic environment. + params: + - description: Reference of the image buildah will produce. + name: IMAGE + type: string + - default: ./Dockerfile + description: Path to the Dockerfile to build. + name: DOCKERFILE + type: string + - default: . + description: Path to the directory to use as context. + name: CONTEXT + type: string + - default: "true" + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) + name: TLSVERIFY + type: string + - default: "false" + description: Determines if build will be executed without network access. + name: HERMETIC + type: string + - default: "" + description: In case it is not empty, the prefetched content should be made available + to the build. + name: PREFETCH_INPUT + type: string + - default: "" + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. + name: IMAGE_EXPIRES_AFTER + type: string + - default: "" + description: The image is built from this commit. + name: COMMIT_SHA + type: string + - default: repos.d + description: Path in the git repository in which yum repository files are stored + name: YUM_REPOS_D_SRC + - default: fetched.repos.d + description: Path in source workspace where dynamically-fetched repos are present + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE + type: string + - default: etc-pki-entitlement + description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET + type: string + - default: activation-key + description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY + type: string + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET + type: string + - default: [] + description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS + type: array + - default: "" + description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE + type: string + - default: trusted-ca + description: The name of the ConfigMap to read CA bundle data from. + name: caTrustConfigMapName + type: string + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey + type: string + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES + type: string + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH + type: string + - default: vfs + description: Storage driver to configure for buildah + name: STORAGE_DRIVER + type: string + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES + type: string + - default: [] + description: Additional key=value labels that should be applied to the image + name: LABELS + type: array + - default: "false" + description: Whether to enable privileged mode + name: PRIVILEGED_NESTED + type: string + - default: "false" + description: Skip SBOM-related operations. This will likely cause EC policies + to fail if enabled + name: SKIP_SBOM_GENERATION + type: string + results: + - description: Digest of the image just built + name: IMAGE_DIGEST + - description: Image repository and tag where the built image was pushed + name: IMAGE_URL + - description: Image reference of the built image + name: IMAGE_REF + - description: Reference of SBOM blob digest to enable digest-based verification + from provenance + name: SBOM_BLOB_URL + type: string + - description: The counting of Java components by publisher in JSON format + name: SBOM_JAVA_COMPONENTS_COUNT + type: string + - description: The Java dependencies that came from community sources such as Maven + central. + name: JAVA_COMMUNITY_DEPENDENCIES + stepTemplate: + computeResources: + limits: + cpu: 500m + memory: 2Gi + requests: + cpu: 100m + memory: 512Mi + env: + - name: BUILDAH_FORMAT + value: oci + - name: STORAGE_DRIVER + value: $(params.STORAGE_DRIVER) + - name: HERMETIC + value: $(params.HERMETIC) + - name: SOURCE_CODE_DIR + value: source + - name: CONTEXT + value: $(params.CONTEXT) + - name: IMAGE + value: $(params.IMAGE) + - name: TLSVERIFY + value: $(params.TLSVERIFY) + - name: IMAGE_EXPIRES_AFTER + value: $(params.IMAGE_EXPIRES_AFTER) + - name: YUM_REPOS_D_SRC + value: $(params.YUM_REPOS_D_SRC) + - name: YUM_REPOS_D_FETCHED + value: $(params.YUM_REPOS_D_FETCHED) + - name: YUM_REPOS_D_TARGET + value: $(params.YUM_REPOS_D_TARGET) + - name: TARGET_STAGE + value: $(params.TARGET_STAGE) + - name: ENTITLEMENT_SECRET + value: $(params.ENTITLEMENT_SECRET) + - name: ACTIVATION_KEY + value: $(params.ACTIVATION_KEY) + - name: ADDITIONAL_SECRET + value: $(params.ADDITIONAL_SECRET) + - name: BUILD_ARGS_FILE + value: $(params.BUILD_ARGS_FILE) + - name: ADD_CAPABILITIES + value: $(params.ADD_CAPABILITIES) + - name: SQUASH + value: $(params.SQUASH) + - name: SKIP_UNUSED_STAGES + value: $(params.SKIP_UNUSED_STAGES) + - name: PRIVILEGED_NESTED + value: $(params.PRIVILEGED_NESTED) + - name: SKIP_SBOM_GENERATION + value: $(params.SKIP_SBOM_GENERATION) + volumeMounts: + - mountPath: /shared + name: shared + steps: + - args: + - --build-args + - $(params.BUILD_ARGS[*]) + - --labels + - $(params.LABELS[*]) + computeResources: + limits: + cpu: 2 + memory: 4Gi + requests: + cpu: 100m + memory: 1Gi + env: + - name: COMMIT_SHA + value: $(params.COMMIT_SHA) + - name: DOCKERFILE + value: $(params.DOCKERFILE) + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build + script: | + #!/bin/bash + set -euo pipefail + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + if [ -e "$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" + elif [ -e "$SOURCE_CODE_DIR/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$DOCKERFILE" + elif [ -e "$DOCKERFILE" ]; then + # Instrumented builds (SAST) use this custom dockerffile step as their base + dockerfile_path="$DOCKERFILE" + elif echo "$DOCKERFILE" | grep -q "^https\?://"; then + echo "Fetch Dockerfile from $DOCKERFILE" + dockerfile_path=$(mktemp --suffix=-Dockerfile) + http_code=$(curl -s -S -L -w "%{http_code}" --output "$dockerfile_path" "$DOCKERFILE") + if [ $http_code != 200 ]; then + echo "No Dockerfile is fetched. Server responds $http_code" + exit 1 + fi + http_code=$(curl -s -S -L -w "%{http_code}" --output "$dockerfile_path.dockerignore.tmp" "$DOCKERFILE.dockerignore") + if [ $http_code = 200 ]; then + echo "Fetched .dockerignore from $DOCKERFILE.dockerignore" + mv "$dockerfile_path.dockerignore.tmp" $SOURCE_CODE_DIR/$CONTEXT/.dockerignore + fi + else + echo "Cannot find Dockerfile $DOCKERFILE" + exit 1 + fi + + dockerfile_copy=$(mktemp --tmpdir "$(basename "$dockerfile_path").XXXXXX") + cp "$dockerfile_path" "$dockerfile_copy" + + if [ -n "${JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR-}" ] && grep -q '^\s*RUN \(./\)\?mvn' "$dockerfile_copy"; then + sed -i -e "s|^\s*RUN \(\(./\)\?mvn\)\(.*\)|RUN echo \"mirror.defaulthttp://$JVM_BUILD_WORKSPACE_ARTIFACT_CACHE_PORT_80_TCP_ADDR/v1/cache/default/0/*\" > /tmp/settings.yaml; \1 -s /tmp/settings.yaml \3|g" "$dockerfile_copy" + touch /var/lib/containers/java + fi + + # Fixing group permission on /var/lib/containers + chown root:root /var/lib/containers + + sed -i 's/^\s*short-name-mode\s*=\s*.*/short-name-mode = "disabled"/' /etc/containers/registries.conf + + # Setting new namespace to run buildah - 2^32-2 + echo 'root:1:4294967294' | tee -a /etc/subuid >> /etc/subgid + + build_args=() + if [ -n "${BUILD_ARGS_FILE}" ]; then + # Parse BUILD_ARGS_FILE ourselves because dockerfile-json doesn't support it + echo "Parsing ARGs from $BUILD_ARGS_FILE" + mapfile -t build_args < <( + # https://www.mankier.com/1/buildah-build#--build-arg-file + # delete lines that start with # + # delete blank lines + sed -e '/^#/d' -e '/^\s*$/d' "${SOURCE_CODE_DIR}/${BUILD_ARGS_FILE}" + ) + fi + + LABELS=() + # Split `args` into two sets of arguments. + while [[ $# -gt 0 ]]; do + case $1 in + --build-args) + shift + # Note: this may result in multiple --build-arg=KEY=value flags with the same KEY being + # passed to buildah. In that case, the *last* occurrence takes precedence. This is why + # we append BUILD_ARGS after the content of the BUILD_ARGS_FILE + while [[ $# -gt 0 && $1 != --* ]]; do build_args+=("$1"); shift; done + ;; + --labels) + shift + while [[ $# -gt 0 && $1 != --* ]]; do LABELS+=("--label" "$1"); shift; done + ;; + *) + echo "unexpected argument: $1" >&2 + exit 2 + ;; + esac + done + + BUILD_ARG_FLAGS=() + for build_arg in "${build_args[@]}"; do + BUILD_ARG_FLAGS+=("--build-arg=$build_arg") + done + + dockerfile-json "${BUILD_ARG_FLAGS[@]}" "$dockerfile_copy" > /shared/parsed_dockerfile.json + BASE_IMAGES=$( + jq -r '.Stages[] | select(.From | .Stage or .Scratch | not) | .BaseName | select(test("^oci-archive:") | not)' /shared/parsed_dockerfile.json + ) + + BUILDAH_ARGS=() + UNSHARE_ARGS=() + + if [ "${HERMETIC}" == "true" ]; then + BUILDAH_ARGS+=("--pull=never") + UNSHARE_ARGS+=("--net") + + for image in $BASE_IMAGES; do + unshare -Ufp --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -- buildah pull $image + done + echo "Build will be executed with network isolation" + fi + + if [ -n "${TARGET_STAGE}" ]; then + BUILDAH_ARGS+=("--target=${TARGET_STAGE}") + fi + + BUILDAH_ARGS+=("${BUILD_ARG_FLAGS[@]}") + + if [ "${PRIVILEGED_NESTED}" == "true" ]; then + BUILDAH_ARGS+=("--security-opt=label=disable") + BUILDAH_ARGS+=("--cap-add=all") + BUILDAH_ARGS+=("--device=/dev/fuse") + fi + + if [ -n "${ADD_CAPABILITIES}" ]; then + BUILDAH_ARGS+=("--cap-add=${ADD_CAPABILITIES}") + fi + + if [ "${SQUASH}" == "true" ]; then + BUILDAH_ARGS+=("--squash") + fi + + if [ "${SKIP_UNUSED_STAGES}" != "true" ] ; then + BUILDAH_ARGS+=("--skip-unused-stages=false") + fi + + VOLUME_MOUNTS=() + + if [ -f "$(workspaces.source.path)/cachi2/cachi2.env" ]; then + cp -r "$(workspaces.source.path)/cachi2" /tmp/ + chmod -R go+rwX /tmp/cachi2 + VOLUME_MOUNTS+=(--volume /tmp/cachi2:/cachi2) + # Read in the whole file (https://unix.stackexchange.com/questions/533277), then + # for each RUN ... line insert the cachi2.env command *after* any options like --mount + sed -E -i \ + -e 'H;1h;$!d;x' \ + -e 's@^\s*(run((\s|\\\n)+-\S+)*(\s|\\\n)+)@\1. /cachi2/cachi2.env \&\& \\\n @igM' \ + "$dockerfile_copy" + echo "Prefetched content will be made available" + + prefetched_repo_for_my_arch="/tmp/cachi2/output/deps/rpm/$(uname -m)/repos.d/cachi2.repo" + if [ -f "$prefetched_repo_for_my_arch" ]; then + echo "Adding $prefetched_repo_for_my_arch to $YUM_REPOS_D_FETCHED" + mkdir -p "$YUM_REPOS_D_FETCHED" + cp --no-clobber "$prefetched_repo_for_my_arch" "$YUM_REPOS_D_FETCHED" + fi + fi + + # if yum repofiles stored in git, copy them to mount point outside the source dir + if [ -d "${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}" ]; then + mkdir -p ${YUM_REPOS_D_FETCHED} + cp -r ${SOURCE_CODE_DIR}/${YUM_REPOS_D_SRC}/* ${YUM_REPOS_D_FETCHED} + fi + + # if anything in the repofiles mount point (either fetched or from git), mount it + if [ -d "${YUM_REPOS_D_FETCHED}" ]; then + chmod -R go+rwX ${YUM_REPOS_D_FETCHED} + mount_point=$(realpath ${YUM_REPOS_D_FETCHED}) + VOLUME_MOUNTS+=(--volume "${mount_point}:${YUM_REPOS_D_TARGET}") + fi + + DEFAULT_LABELS=( + "--label" "build-date=$(date -u +'%Y-%m-%dT%H:%M:%S')" + "--label" "architecture=$(uname -m)" + "--label" "vcs-type=git" + ) + [ -n "$COMMIT_SHA" ] && DEFAULT_LABELS+=("--label" "vcs-ref=$COMMIT_SHA") + [ -n "$IMAGE_EXPIRES_AFTER" ] && DEFAULT_LABELS+=("--label" "quay.expires-after=$IMAGE_EXPIRES_AFTER") + + # Concatenate defaults and explicit labels. If a label appears twice, the last one wins. + LABELS=("${DEFAULT_LABELS[@]}" "${LABELS[@]}") + + ACTIVATION_KEY_PATH="/activation-key" + ENTITLEMENT_PATH="/entitlement" + + # 0. if hermetic=true, skip all subscription related stuff + # 1. do not enable activation key and entitlement at same time. If both vars are provided, prefer activation key. + # 2. Activation-keys will be used when the key 'org' exists in the activation key secret. + # 3. try to pre-register and mount files to the correct location so that users do no need to modify Dockerfiles. + # 3. If the Dockerfile contains the string "subcription-manager register", add the activation-keys volume + # to buildah but don't pre-register for backwards compatibility. Mount an empty directory on + # shared emptydir volume to "/etc/pki/entitlement" to prevent certificates from being included + + if [ "${HERMETIC}" != "true" ] && [ -e /activation-key/org ]; then + cp -r --preserve=mode "$ACTIVATION_KEY_PATH" /tmp/activation-key + mkdir -p /shared/rhsm/etc/pki/entitlement + mkdir -p /shared/rhsm/etc/pki/consumer + + VOLUME_MOUNTS+=(-v /tmp/activation-key:/activation-key \ + -v /shared/rhsm/etc/pki/entitlement:/etc/pki/entitlement:Z \ + -v /shared/rhsm/etc/pki/consumer:/etc/pki/consumer:Z) + echo "Adding activation key to the build" + + if ! grep -E "^[^#]*subscription-manager.[^#]*register" "$dockerfile_path"; then + # user is not running registration in the Containerfile: pre-register. + echo "Pre-registering with subscription manager." + subscription-manager register --org "$(cat /tmp/activation-key/org)" --activationkey "$(cat /tmp/activation-key/activationkey)" + trap 'subscription-manager unregister || true' EXIT + + # copy generated certificates to /shared volume + cp /etc/pki/entitlement/*.pem /shared/rhsm/etc/pki/entitlement + cp /etc/pki/consumer/*.pem /shared/rhsm/etc/pki/consumer + + # and then mount get /etc/rhsm/ca/redhat-uep.pem into /run/secrets/rhsm/ca + VOLUME_MOUNTS+=(--volume /etc/rhsm/ca/redhat-uep.pem:/etc/rhsm/ca/redhat-uep.pem:Z) + fi + + elif [ "${HERMETIC}" != "true" ] && find /entitlement -name "*.pem" >> null; then + cp -r --preserve=mode "$ENTITLEMENT_PATH" /tmp/entitlement + VOLUME_MOUNTS+=(--volume /tmp/entitlement:/etc/pki/entitlement) + echo "Adding the entitlement to the build" + fi + + if [ -n "${ADDITIONAL_VOLUME_MOUNTS-}" ]; then + # ADDITIONAL_VOLUME_MOUNTS allows to specify more volumes for the build. + # Instrumented builds (SAST) use this step as their base and add some other tools. + while read -r volume_mount; do + VOLUME_MOUNTS+=("--volume=$volume_mount") + done <<< "$ADDITIONAL_VOLUME_MOUNTS" + fi + + ADDITIONAL_SECRET_PATH="/additional-secret" + ADDITIONAL_SECRET_TMP="/tmp/additional-secret" + if [ -d "$ADDITIONAL_SECRET_PATH" ]; then + cp -r --preserve=mode -L "$ADDITIONAL_SECRET_PATH" $ADDITIONAL_SECRET_TMP + while read -r filename; do + echo "Adding the secret ${ADDITIONAL_SECRET}/${filename} to the build, available at /run/secrets/${ADDITIONAL_SECRET}/${filename}" + BUILDAH_ARGS+=("--secret=id=${ADDITIONAL_SECRET}/${filename},src=$ADDITIONAL_SECRET_TMP/${filename}") + done < <(find $ADDITIONAL_SECRET_TMP -maxdepth 1 -type f -exec basename {} \;) + fi + + # Prevent ShellCheck from giving a warning because 'image' is defined and 'IMAGE' is not. + declare IMAGE + + buildah_cmd_array=( + buildah build + "${VOLUME_MOUNTS[@]}" + "${BUILDAH_ARGS[@]}" + "${LABELS[@]}" + --tls-verify="$TLSVERIFY" --no-cache + --ulimit nofile=4096:4096 + -f "$dockerfile_copy" -t "$IMAGE" . + ) + buildah_cmd=$(printf "%q " "${buildah_cmd_array[@]}") + + if [ "${HERMETIC}" == "true" ]; then + # enabling loopback adapter enables Bazel builds to work in hermetic mode. + command="ip link set lo up && $buildah_cmd" + else + command="$buildah_cmd" + fi + + # disable host subcription manager integration + find /usr/share/rhel/secrets -type l -exec unlink {} \; + + unshare -Uf "${UNSHARE_ARGS[@]}" --keep-caps -r --map-users 1,1,65536 --map-groups 1,1,65536 -w "${SOURCE_CODE_DIR}/$CONTEXT" -- sh -c "$command" + + container=$(buildah from --pull-never "$IMAGE") + + # Save the SBOM produced by Cachi2 so it can be merged into the final SBOM later + if [ -f "/tmp/cachi2/output/bom.json" ]; then + echo "Making copy of sbom-cachi2.json" + cp /tmp/cachi2/output/bom.json ./sbom-cachi2.json + fi + + buildah mount $container | tee /shared/container_path + # delete symlinks - they may point outside the container rootfs, messing with SBOM scanners + find $(cat /shared/container_path) -xtype l -delete + echo $container > /shared/container_name + + touch /shared/base_images_digests + echo "Recording base image digests used" + for image in $BASE_IMAGES; do + base_image_digest=$(buildah images --format '{{ .Name }}:{{ .Tag }}@{{ .Digest }}' --filter reference="$image") + # In some cases, there might be BASE_IMAGES, but not any associated digest. This happens + # if buildah did not use that particular image during build because it was skipped + if [ -n "$base_image_digest" ]; then + echo "$image $base_image_digest" | tee -a /shared/base_images_digests + fi + done + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /entitlement + name: etc-pki-entitlement + - mountPath: /activation-key + name: activation-key + - mountPath: /additional-secret + name: additional-secret + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/icm-injection-scripts:latest@sha256:523689a26c74790be67d6036820abfb9aa10bec6efff98cc1c9b74bd2f99eeb1 + name: icm + script: | + #!/bin/bash + set -euo pipefail + /scripts/inject-icm.sh "$IMAGE" + securityContext: + capabilities: + add: + - SETFCAP + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: push + script: | + #!/bin/bash + set -e + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + retries=5 + # Push to a unique tag based on the TaskRun name to avoid race conditions + echo "Pushing to ${IMAGE%:*}:${TASKRUN_NAME}" + if ! buildah push \ + --retry "$retries" \ + --tls-verify="$TLSVERIFY" \ + "$IMAGE" \ + "docker://${IMAGE%:*}:$(context.taskRun.name)"; + then + echo "Failed to push sbom image to ${IMAGE%:*}:$(context.taskRun.name) after ${retries} tries" + exit 1 + fi + + # Push to a tag based on the git revision + echo "Pushing to ${IMAGE}" + if ! buildah push \ + --retry "$retries" \ + --tls-verify="$TLSVERIFY" \ + --digestfile "$(workspaces.source.path)/image-digest" "$IMAGE" \ + "docker://$IMAGE"; + then + echo "Failed to push sbom image to $IMAGE after ${retries} tries" + exit 1 + fi + + cat "$(workspaces.source.path)"/image-digest | tee $(results.IMAGE_DIGEST.path) + echo -n "$IMAGE" | tee $(results.IMAGE_URL.path) + { + echo -n "${IMAGE}@" + cat "$(workspaces.source.path)/image-digest" + } > "$(results.IMAGE_REF.path)" + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate + script: | + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + echo "Running syft on the source directory" + syft dir:"$(workspaces.source.path)/$SOURCE_CODE_DIR/$CONTEXT" --output cyclonedx-json@1.5="$(workspaces.source.path)/sbom-source.json" + echo "Running syft on the image filesystem" + syft dir:"$(cat /shared/container_path)" --output cyclonedx-json@1.5="$(workspaces.source.path)/sbom-image.json" + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + workingDir: $(workspaces.source.path)/source + - computeResources: + limits: + cpu: 100m + memory: 256Mi + requests: + cpu: 10m + memory: 128Mi + image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + name: analyse-dependencies-java-sbom + script: | + if [ -f /var/lib/containers/java ]; then + /opt/jboss/container/java/run/run-java.sh analyse-dependencies path $(cat /shared/container_path) -s $(workspaces.source.path)/sbom-image.json --task-run-name $(context.taskRun.name) --publishers $(results.SBOM_JAVA_COMPONENTS_COUNT.path) + sed -i 's/^/ /' $(results.SBOM_JAVA_COMPONENTS_COUNT.path) # Workaround for SRVKP-2875 + else + touch $(results.JAVA_COMMUNITY_DEPENDENCIES.path) + fi + securityContext: + runAsUser: 0 + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /shared + name: shared + - computeResources: + limits: + cpu: 100m + memory: 256Mi + requests: + cpu: 10m + memory: 128Mi + image: quay.io/konflux-ci/sbom-utility-scripts@sha256:7f4ce55033077f37eda61c67b4289949471c36d23ae0cc66fd4113e615b3ef93 + name: prepare-sboms + script: | + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + echo "Merging contents of sbom-source.json and sbom-image.json into sbom-cyclonedx.json" + python3 /scripts/merge_syft_sboms.py + + if [ -f "sbom-cachi2.json" ]; then + echo "Merging contents of sbom-cachi2.json into sbom-cyclonedx.json" + python3 /scripts/merge_cachi2_sboms.py sbom-cachi2.json sbom-cyclonedx.json > sbom-temp.json + mv sbom-temp.json sbom-cyclonedx.json + fi + + echo "Adding base images data to sbom-cyclonedx.json" + python3 /scripts/base_images_sbom_script.py \ + --sbom=sbom-cyclonedx.json \ + --parsed-dockerfile=/shared/parsed_dockerfile.json \ + --base-images-digests=/shared/base_images_digests + + echo "Adding image reference to sbom" + IMAGE_URL="$(cat "$(results.IMAGE_URL.path)")" + IMAGE_DIGEST="$(cat "$(results.IMAGE_DIGEST.path)")" + + python3 /scripts/add_image_reference.py \ + --image-url "$IMAGE_URL" \ + --image-digest "$IMAGE_DIGEST" \ + --input-file sbom-cyclonedx.json \ + --output-file /tmp/sbom-cyclonedx.tmp.json + + mv /tmp/sbom-cyclonedx.tmp.json sbom-cyclonedx.json + securityContext: + runAsUser: 0 + workingDir: $(workspaces.source.path) + - computeResources: + limits: + cpu: 2 + memory: 2Gi + requests: + cpu: 100m + memory: 512Mi + image: quay.io/konflux-ci/appstudio-utils:48c311af02858e2422d6229600e9959e496ddef1@sha256:91ddd999271f65d8ec8487b10f3dd378f81aa894e11b9af4d10639fd52bba7e8 + name: upload-sbom + script: | + #!/bin/bash + if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then + echo "Skipping SBOM generation" + exit 0 + fi + + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + cosign attach sbom --sbom sbom-cyclonedx.json --type cyclonedx "$(cat "$(results.IMAGE_REF.path)")" + + # Remove tag from IMAGE while allowing registry to contain a port number. + sbom_repo="${IMAGE%:*}" + sbom_digest="$(sha256sum sbom-cyclonedx.json | cut -d' ' -f1)" + # The SBOM_BLOB_URL is created by `cosign attach sbom`. + echo -n "${sbom_repo}@sha256:${sbom_digest}" | tee "$(results.SBOM_BLOB_URL.path)" + volumeMounts: + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + workingDir: $(workspaces.source.path) + volumes: + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared + - name: etc-pki-entitlement + secret: + optional: true + secretName: $(params.ENTITLEMENT_SECRET) + - name: activation-key + secret: + optional: true + secretName: $(params.ACTIVATION_KEY) + - name: additional-secret + secret: + optional: true + secretName: $(params.ADDITIONAL_SECRET) + - configMap: + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) + optional: true + name: trusted-ca + workspaces: + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah-oci-ta/0.1/kustomization.yaml b/task/buildah-oci-ta/0.1/kustomization.yaml deleted file mode 100644 index 8f0d97f03d..0000000000 --- a/task/buildah-oci-ta/0.1/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: -- buildah-oci-ta.yaml diff --git a/task/buildah/0.1/buildah.yaml b/task/buildah/0.1/buildah.yaml index 10c2bff39c..7abd3bce86 100644 --- a/task/buildah/0.1/buildah.yaml +++ b/task/buildah/0.1/buildah.yaml @@ -1,13 +1,13 @@ apiVersion: tekton.dev/v1 kind: Task metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2024-11-13T00:00:00Z" + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux labels: app.kubernetes.io/version: "0.1" - build.appstudio.redhat.com/build_type: "docker" - annotations: - tekton.dev/pipelines.minVersion: "0.12.1" - tekton.dev/tags: "image-build, konflux" - build.appstudio.redhat.com/expires-on: 2024-11-13T00:00:00Z + build.appstudio.redhat.com/build_type: docker name: buildah spec: description: |- @@ -32,87 +32,97 @@ spec: name: CONTEXT type: string - default: "true" - description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS registry) + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) name: TLSVERIFY type: string - - description: unused, should be removed in next task version + - default: "" + description: unused, should be removed in next task version name: DOCKER_AUTH type: string - default: "" - default: "false" description: Determines if build will be executed without network access. name: HERMETIC type: string - default: "" - description: In case it is not empty, the prefetched content should be made available to the build. + description: In case it is not empty, the prefetched content should be made available + to the build. name: PREFETCH_INPUT type: string - default: "" - description: Delete image tag after specified time. Empty means to keep the image tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, respectively. + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. name: IMAGE_EXPIRES_AFTER type: string - - name: COMMIT_SHA + - default: "" description: The image is built from this commit. + name: COMMIT_SHA type: string - default: "" - - name: YUM_REPOS_D_SRC + - default: repos.d description: Path in the git repository in which yum repository files are stored - default: repos.d - - name: YUM_REPOS_D_FETCHED + name: YUM_REPOS_D_SRC + - default: fetched.repos.d description: Path in source workspace where dynamically-fetched repos are present - default: fetched.repos.d - - name: YUM_REPOS_D_TARGET - description: Target path on the container in which yum repository files should be made available - default: /etc/yum.repos.d - - name: TARGET_STAGE - description: Target stage in Dockerfile to build. If not specified, the Dockerfile is processed entirely to (and including) its last stage. + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE type: string - default: "" - - name: ENTITLEMENT_SECRET + - default: etc-pki-entitlement description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET type: string - default: "etc-pki-entitlement" - - name: ACTIVATION_KEY - default: activation-key + - default: activation-key description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY type: string - - name: ADDITIONAL_SECRET - description: Name of a secret which will be made available to the build with 'buildah build --secret' at /run/secrets/$ADDITIONAL_SECRET + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET type: string - default: "does-not-exist" - - name: BUILD_ARGS + - default: [] description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS type: array - default: [] - - name: BUILD_ARGS_FILE + - default: "" description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE type: string - default: "" - - name: caTrustConfigMapName - type: string + - default: trusted-ca description: The name of the ConfigMap to read CA bundle data from. - default: trusted-ca - - name: caTrustConfigMapKey + name: caTrustConfigMapName type: string - description: The name of the key in the ConfigMap that contains the CA bundle data. - default: ca-bundle.crt - - name: ADD_CAPABILITIES - description: Comma separated list of extra capabilities to add when running 'buildah build' + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey type: string - default: "" - - name: SQUASH - description: Squash all new and previous layers added as a part of this build, as per --squash + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES + type: string + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH type: string - default: "false" - - name: STORAGE_DRIVER + - default: vfs description: Storage driver to configure for buildah + name: STORAGE_DRIVER type: string - default: vfs - - name: SKIP_UNUSED_STAGES - description: Whether to skip stages in Containerfile that seem unused by subsequent stages + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES type: string - default: "true" - results: - description: Digest of the image just built name: IMAGE_DIGEST @@ -120,15 +130,13 @@ spec: name: IMAGE_URL - description: Digests of the base images used for build name: BASE_IMAGES_DIGESTS - - name: SBOM_JAVA_COMPONENTS_COUNT - description: The counting of Java components by publisher in JSON format + - description: The counting of Java components by publisher in JSON format + name: SBOM_JAVA_COMPONENTS_COUNT type: string - - name: JAVA_COMMUNITY_DEPENDENCIES - description: The Java dependencies that came from community sources such as Maven central. + - description: The Java dependencies that came from community sources such as Maven + central. + name: JAVA_COMMUNITY_DEPENDENCIES stepTemplate: - volumeMounts: - - mountPath: /shared - name: shared env: - name: BUILDAH_FORMAT value: oci @@ -168,21 +176,23 @@ spec: value: $(params.SQUASH) - name: SKIP_UNUSED_STAGES value: $(params.SKIP_UNUSED_STAGES) - + volumeMounts: + - mountPath: /shared + name: shared steps: - - image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c - name: build + - args: + - $(params.BUILD_ARGS[*]) computeResources: limits: memory: 4Gi requests: - memory: 512Mi cpu: 250m + memory: 512Mi env: - name: COMMIT_SHA value: $(params.COMMIT_SHA) - args: - - $(params.BUILD_ARGS[*]) + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build script: | if [ -n "${PARAM_BUILDER_IMAGE}" ]; then echo "WARNING: provided deprecated BUILDER_IMAGE parameter has no effect." @@ -363,30 +373,25 @@ spec: # Needed to generate base images SBOM echo "$BASE_IMAGES" > $(workspaces.source.path)/base_images_from_dockerfile - securityContext: capabilities: add: - - SETFCAP + - SETFCAP volumeMounts: - mountPath: /var/lib/containers name: varlibcontainers - - mountPath: "/entitlement" + - mountPath: /entitlement name: etc-pki-entitlement - mountPath: /activation-key name: activation-key - - mountPath: "/additional-secret" + - mountPath: /additional-secret name: additional-secret - - name: trusted-ca - mountPath: /mnt/trusted-ca + - mountPath: /mnt/trusted-ca + name: trusted-ca readOnly: true workingDir: $(workspaces.source.path) - - - name: sbom-syft-generate - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c - # Respect Syft configuration if the user has it in the root of their repository - # (need to set the workdir, see https://github.com/anchore/syft/issues/2465) - workingDir: $(workspaces.source.path)/source + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate script: | echo "Running syft on the source directory" syft dir:$(workspaces.source.path)/source --output cyclonedx-json=$(workspaces.source.path)/sbom-source.json @@ -397,8 +402,9 @@ spec: name: varlibcontainers - mountPath: /shared name: shared - - name: analyse-dependencies-java-sbom - image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + workingDir: $(workspaces.source.path)/source + - image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + name: analyse-dependencies-java-sbom script: | if [ -f /var/lib/containers/java ]; then /opt/jboss/container/java/run/run-java.sh analyse-dependencies path $(cat /shared/container_path) -s $(workspaces.source.path)/sbom-image.json --task-run-name $(context.taskRun.name) --publishers $(results.SBOM_JAVA_COMPONENTS_COUNT.path) @@ -406,16 +412,15 @@ spec: else touch $(results.JAVA_COMMUNITY_DEPENDENCIES.path) fi + securityContext: + runAsUser: 0 volumeMounts: - mountPath: /var/lib/containers name: varlibcontainers - mountPath: /shared name: shared - securityContext: - runAsUser: 0 - - - name: merge-syft-sboms - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + name: merge-syft-sboms script: | #!/bin/python3 import json @@ -445,12 +450,11 @@ spec: # write the CycloneDX unified SBOM with open("./sbom-cyclonedx.json", "w") as f: json.dump(image_sbom, f, indent=4) - workingDir: $(workspaces.source.path) securityContext: runAsUser: 0 - - - name: merge-cachi2-sbom - image: quay.io/konflux-ci/cachi2:0.17.0@sha256:963870f04aeb4a207e79b8eacb47e16c8faa7451f52e92959e0e16cdbd258fb3 + workingDir: $(workspaces.source.path) + - image: quay.io/konflux-ci/cachi2:0.17.0@sha256:963870f04aeb4a207e79b8eacb47e16c8faa7451f52e92959e0e16cdbd258fb3 + name: merge-cachi2-sbom script: | if [ -f "sbom-cachi2.json" ]; then echo "Merging contents of sbom-cachi2.json into sbom-cyclonedx.json" @@ -459,12 +463,11 @@ spec: else echo "Skipping step since no Cachi2 SBOM was produced" fi - workingDir: $(workspaces.source.path) securityContext: runAsUser: 0 - - - name: create-purl-sbom - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + workingDir: $(workspaces.source.path) + - image: registry.access.redhat.com/ubi9/python-39:9.5-1736743782@sha256:36ae15329ade62cc7082d44db67f94494bf3836edb8b7cf733f7519168a6e9de + name: create-purl-sbom script: | #!/bin/python3 import json @@ -477,24 +480,22 @@ spec: with open("sbom-purl.json", "w") as output_file: json.dump(purl_content, output_file, indent=4) - workingDir: $(workspaces.source.path) securityContext: runAsUser: 0 - - - name: create-base-images-sbom - image: quay.io/redhat-appstudio/base-images-sbom-script@sha256:667669e3def018f9dbb8eaf8868887a40bc07842221e9a98f6787edcff021840 - env: + workingDir: $(workspaces.source.path) + - env: - name: BASE_IMAGES_DIGESTS_PATH value: $(results.BASE_IMAGES_DIGESTS.path) + image: quay.io/redhat-appstudio/base-images-sbom-script@sha256:667669e3def018f9dbb8eaf8868887a40bc07842221e9a98f6787edcff021840 + name: create-base-images-sbom script: | python3 /app/base_images_sbom_script.py --sbom=sbom-cyclonedx.json --base-images-from-dockerfile=base_images_from_dockerfile --base-images-digests=$BASE_IMAGES_DIGESTS_PATH - workingDir: $(workspaces.source.path) securityContext: runAsUser: 0 - - - name: inject-sbom-and-push + workingDir: $(workspaces.source.path) + - computeResources: {} image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c - computeResources: {} + name: inject-sbom-and-push script: | if [ -n "${PARAM_BUILDER_IMAGE}" ]; then echo "WARNING: provided deprecated BUILDER_IMAGE parameter has no effect." @@ -532,52 +533,50 @@ spec: cat "$(workspaces.source.path)"/image-digest | tee $(results.IMAGE_DIGEST.path) echo -n "$IMAGE" | tee $(results.IMAGE_URL.path) - securityContext: - runAsUser: 0 capabilities: add: - - SETFCAP + - SETFCAP + runAsUser: 0 volumeMounts: - mountPath: /var/lib/containers name: varlibcontainers workingDir: $(workspaces.source.path) - - - name: upload-sbom + - args: + - attach + - sbom + - --sbom + - sbom-cyclonedx.json + - --type + - cyclonedx + - $(params.IMAGE) image: quay.io/redhat-appstudio/cosign:v2.1.1@sha256:c883d6f8d39148f2cea71bff4622d196d89df3e510f36c140c097b932f0dd5d5 - args: - - attach - - sbom - - --sbom - - sbom-cyclonedx.json - - --type - - cyclonedx - - $(params.IMAGE) + name: upload-sbom workingDir: $(workspaces.source.path) volumes: - - name: varlibcontainers - emptyDir: {} - - name: shared - emptyDir: {} + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared - name: etc-pki-entitlement secret: - secretName: $(params.ENTITLEMENT_SECRET) optional: true + secretName: $(params.ENTITLEMENT_SECRET) - name: activation-key secret: optional: true secretName: $(params.ACTIVATION_KEY) - name: additional-secret secret: - secretName: $(params.ADDITIONAL_SECRET) optional: true - - name: trusted-ca - configMap: - name: $(params.caTrustConfigMapName) + secretName: $(params.ADDITIONAL_SECRET) + - configMap: items: - - key: $(params.caTrustConfigMapKey) - path: ca-bundle.crt + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) optional: true + name: trusted-ca workspaces: - - name: source - description: Workspace containing the source code to build. + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah/0.2/buildah.yaml b/task/buildah/0.2/buildah.yaml index bdb57538a3..2c7b7b0d08 100644 --- a/task/buildah/0.2/buildah.yaml +++ b/task/buildah/0.2/buildah.yaml @@ -1,12 +1,12 @@ apiVersion: tekton.dev/v1 kind: Task metadata: - labels: - app.kubernetes.io/version: "0.2.1" - build.appstudio.redhat.com/build_type: "docker" annotations: - tekton.dev/pipelines.minVersion: "0.12.1" - tekton.dev/tags: "image-build, konflux" + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: 0.2.1 + build.appstudio.redhat.com/build_type: docker name: buildah spec: description: |- @@ -27,7 +27,8 @@ spec: name: CONTEXT type: string - default: "true" - description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS registry) + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) name: TLSVERIFY type: string - default: "false" @@ -35,87 +36,97 @@ spec: name: HERMETIC type: string - default: "" - description: In case it is not empty, the prefetched content should be made available to the build. + description: In case it is not empty, the prefetched content should be made available + to the build. name: PREFETCH_INPUT type: string - default: "" - description: Delete image tag after specified time. Empty means to keep the image tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, respectively. + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. name: IMAGE_EXPIRES_AFTER type: string - - name: COMMIT_SHA + - default: "" description: The image is built from this commit. + name: COMMIT_SHA type: string - default: "" - - name: YUM_REPOS_D_SRC + - default: repos.d description: Path in the git repository in which yum repository files are stored - default: repos.d - - name: YUM_REPOS_D_FETCHED + name: YUM_REPOS_D_SRC + - default: fetched.repos.d description: Path in source workspace where dynamically-fetched repos are present - default: fetched.repos.d - - name: YUM_REPOS_D_TARGET - description: Target path on the container in which yum repository files should be made available - default: /etc/yum.repos.d - - name: TARGET_STAGE - description: Target stage in Dockerfile to build. If not specified, the Dockerfile is processed entirely to (and including) its last stage. + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE type: string - default: "" - - name: ENTITLEMENT_SECRET + - default: etc-pki-entitlement description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET type: string - default: "etc-pki-entitlement" - - name: ACTIVATION_KEY - default: activation-key + - default: activation-key description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY type: string - - name: ADDITIONAL_SECRET - description: Name of a secret which will be made available to the build with 'buildah build --secret' at /run/secrets/$ADDITIONAL_SECRET + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET type: string - default: "does-not-exist" - - name: BUILD_ARGS + - default: [] description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS type: array - default: [] - - name: BUILD_ARGS_FILE + - default: "" description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE type: string - default: "" - - name: caTrustConfigMapName - type: string + - default: trusted-ca description: The name of the ConfigMap to read CA bundle data from. - default: trusted-ca - - name: caTrustConfigMapKey + name: caTrustConfigMapName type: string - description: The name of the key in the ConfigMap that contains the CA bundle data. - default: ca-bundle.crt - - name: ADD_CAPABILITIES - description: Comma separated list of extra capabilities to add when running 'buildah build' + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey type: string - default: "" - - name: SQUASH - description: Squash all new and previous layers added as a part of this build, as per --squash + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES + type: string + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH type: string - default: "false" - - name: STORAGE_DRIVER + - default: vfs description: Storage driver to configure for buildah + name: STORAGE_DRIVER type: string - default: vfs - - name: SKIP_UNUSED_STAGES - description: Whether to skip stages in Containerfile that seem unused by subsequent stages + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES type: string - default: "true" - - name: LABELS + - default: [] description: Additional key=value labels that should be applied to the image + name: LABELS type: array - default: [] - - name: PRIVILEGED_NESTED + - default: "false" description: Whether to enable privileged mode + name: PRIVILEGED_NESTED type: string - default: "false" - - name: SKIP_SBOM_GENERATION - description: Skip SBOM-related operations. This will likely cause EC policies to fail if enabled + - default: "false" + description: Skip SBOM-related operations. This will likely cause EC policies + to fail if enabled + name: SKIP_SBOM_GENERATION type: string - default: "false" - results: - description: Digest of the image just built name: IMAGE_DIGEST @@ -123,25 +134,24 @@ spec: name: IMAGE_URL - description: Image reference of the built image name: IMAGE_REF - - name: SBOM_BLOB_URL - description: Reference of SBOM blob digest to enable digest-based verification from provenance + - description: Reference of SBOM blob digest to enable digest-based verification + from provenance + name: SBOM_BLOB_URL type: string - - name: SBOM_JAVA_COMPONENTS_COUNT - description: The counting of Java components by publisher in JSON format + - description: The counting of Java components by publisher in JSON format + name: SBOM_JAVA_COMPONENTS_COUNT type: string - - name: JAVA_COMMUNITY_DEPENDENCIES - description: The Java dependencies that came from community sources such as Maven central. + - description: The Java dependencies that came from community sources such as Maven + central. + name: JAVA_COMMUNITY_DEPENDENCIES stepTemplate: computeResources: limits: + cpu: "4" memory: 4Gi - cpu: '4' requests: + cpu: "1" memory: 1Gi - cpu: '1' - volumeMounts: - - mountPath: /shared - name: shared env: - name: BUILDAH_FORMAT value: oci @@ -185,28 +195,29 @@ spec: value: $(params.PRIVILEGED_NESTED) - name: SKIP_SBOM_GENERATION value: $(params.SKIP_SBOM_GENERATION) - + volumeMounts: + - mountPath: /shared + name: shared steps: - - image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c - name: build + - args: + - --build-args + - $(params.BUILD_ARGS[*]) + - --labels + - $(params.LABELS[*]) computeResources: limits: + cpu: "4" memory: 8Gi - cpu: '4' requests: + cpu: "1" memory: 2Gi - cpu: '1' env: - name: COMMIT_SHA value: $(params.COMMIT_SHA) - name: DOCKERFILE value: $(params.DOCKERFILE) - args: - - --build-args - - $(params.BUILD_ARGS[*]) - - --labels - - $(params.LABELS[*]) - + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build script: | #!/bin/bash set -euo pipefail @@ -493,40 +504,39 @@ spec: echo "$image $base_image_digest" | tee -a /shared/base_images_digests fi done - securityContext: capabilities: add: - - SETFCAP + - SETFCAP volumeMounts: - mountPath: /var/lib/containers name: varlibcontainers - - mountPath: "/entitlement" + - mountPath: /entitlement name: etc-pki-entitlement - mountPath: /activation-key name: activation-key - - mountPath: "/additional-secret" + - mountPath: /additional-secret name: additional-secret - - name: trusted-ca - mountPath: /mnt/trusted-ca + - mountPath: /mnt/trusted-ca + name: trusted-ca readOnly: true workingDir: $(workspaces.source.path) - - name: icm - image: quay.io/konflux-ci/icm-injection-scripts:latest@sha256:523689a26c74790be67d6036820abfb9aa10bec6efff98cc1c9b74bd2f99eeb1 + - image: quay.io/konflux-ci/icm-injection-scripts:latest@sha256:523689a26c74790be67d6036820abfb9aa10bec6efff98cc1c9b74bd2f99eeb1 + name: icm + script: | + #!/bin/bash + set -euo pipefail + /scripts/inject-icm.sh "$IMAGE" securityContext: capabilities: add: - - SETFCAP + - SETFCAP volumeMounts: - mountPath: /var/lib/containers name: varlibcontainers workingDir: $(workspaces.source.path) - script: | - #!/bin/bash - set -euo pipefail - /scripts/inject-icm.sh "$IMAGE" - - name: push - image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + - image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: push script: | #!/bin/bash set -e @@ -569,25 +579,20 @@ spec: echo -n "${IMAGE}@" cat "$(workspaces.source.path)/image-digest" } > "$(results.IMAGE_REF.path)" - securityContext: - runAsUser: 0 capabilities: add: - - SETFCAP + - SETFCAP + runAsUser: 0 volumeMounts: - mountPath: /var/lib/containers name: varlibcontainers - - name: trusted-ca - mountPath: /mnt/trusted-ca + - mountPath: /mnt/trusted-ca + name: trusted-ca readOnly: true workingDir: $(workspaces.source.path) - - - name: sbom-syft-generate - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c - # Respect Syft configuration if the user has it in the root of their repository - # (need to set the workdir, see https://github.com/anchore/syft/issues/2465) - workingDir: $(workspaces.source.path)/source + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate script: | if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then echo "Skipping SBOM generation" @@ -602,15 +607,16 @@ spec: name: varlibcontainers - mountPath: /shared name: shared - - name: analyse-dependencies-java-sbom - image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 - computeResources: + workingDir: $(workspaces.source.path)/source + - computeResources: limits: - memory: 512Mi cpu: 200m + memory: 512Mi requests: - memory: 256Mi cpu: 100m + memory: 256Mi + image: quay.io/redhat-appstudio/hacbs-jvm-build-request-processor:127ee0c223a2b56a9bd20a6f2eaeed3bd6015f77 + name: analyse-dependencies-java-sbom script: | if [ -f /var/lib/containers/java ]; then /opt/jboss/container/java/run/run-java.sh analyse-dependencies path $(cat /shared/container_path) -s $(workspaces.source.path)/sbom-image.json --task-run-name $(context.taskRun.name) --publishers $(results.SBOM_JAVA_COMPONENTS_COUNT.path) @@ -618,23 +624,22 @@ spec: else touch $(results.JAVA_COMMUNITY_DEPENDENCIES.path) fi + securityContext: + runAsUser: 0 volumeMounts: - mountPath: /var/lib/containers name: varlibcontainers - mountPath: /shared name: shared - securityContext: - runAsUser: 0 - - - name: prepare-sboms - image: quay.io/konflux-ci/sbom-utility-scripts@sha256:7f4ce55033077f37eda61c67b4289949471c36d23ae0cc66fd4113e615b3ef93 - computeResources: + - computeResources: limits: - memory: 512Mi cpu: 200m + memory: 512Mi requests: - memory: 256Mi cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/sbom-utility-scripts@sha256:7f4ce55033077f37eda61c67b4289949471c36d23ae0cc66fd4113e615b3ef93 + name: prepare-sboms script: | if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then echo "Skipping SBOM generation" @@ -666,12 +671,18 @@ spec: --output-file /tmp/sbom-cyclonedx.tmp.json mv /tmp/sbom-cyclonedx.tmp.json sbom-cyclonedx.json - workingDir: $(workspaces.source.path) securityContext: runAsUser: 0 - - - name: upload-sbom + workingDir: $(workspaces.source.path) + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi image: quay.io/konflux-ci/appstudio-utils:48c311af02858e2422d6229600e9959e496ddef1@sha256:91ddd999271f65d8ec8487b10f3dd378f81aa894e11b9af4d10639fd52bba7e8 + name: upload-sbom script: | #!/bin/bash if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then @@ -693,43 +704,35 @@ spec: sbom_digest="$(sha256sum sbom-cyclonedx.json | cut -d' ' -f1)" # The SBOM_BLOB_URL is created by `cosign attach sbom`. echo -n "${sbom_repo}@sha256:${sbom_digest}" | tee "$(results.SBOM_BLOB_URL.path)" - computeResources: - limits: - memory: 512Mi - cpu: 200m - requests: - memory: 256Mi - cpu: 100m volumeMounts: - - name: trusted-ca - mountPath: /mnt/trusted-ca + - mountPath: /mnt/trusted-ca + name: trusted-ca readOnly: true workingDir: $(workspaces.source.path) - volumes: - - name: varlibcontainers - emptyDir: {} - - name: shared - emptyDir: {} + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared - name: etc-pki-entitlement secret: - secretName: $(params.ENTITLEMENT_SECRET) optional: true + secretName: $(params.ENTITLEMENT_SECRET) - name: activation-key secret: optional: true secretName: $(params.ACTIVATION_KEY) - name: additional-secret secret: - secretName: $(params.ADDITIONAL_SECRET) optional: true - - name: trusted-ca - configMap: - name: $(params.caTrustConfigMapName) + secretName: $(params.ADDITIONAL_SECRET) + - configMap: items: - - key: $(params.caTrustConfigMapKey) - path: ca-bundle.crt + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) optional: true + name: trusted-ca workspaces: - - name: source - description: Workspace containing the source code to build. + - description: Workspace containing the source code to build. + name: source diff --git a/task/buildah/0.3/buildah.yaml b/task/buildah/0.3/buildah.yaml index 29294bfb9a..622b26a568 100644 --- a/task/buildah/0.3/buildah.yaml +++ b/task/buildah/0.3/buildah.yaml @@ -1,12 +1,12 @@ apiVersion: tekton.dev/v1 kind: Task metadata: - labels: - app.kubernetes.io/version: "0.2.1" - build.appstudio.redhat.com/build_type: "docker" annotations: - tekton.dev/pipelines.minVersion: "0.12.1" - tekton.dev/tags: "image-build, konflux" + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: image-build, konflux + labels: + app.kubernetes.io/version: 0.2.1 + build.appstudio.redhat.com/build_type: docker name: buildah spec: description: |- @@ -26,7 +26,8 @@ spec: name: CONTEXT type: string - default: "true" - description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS registry) + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS + registry) name: TLSVERIFY type: string - default: "false" @@ -34,87 +35,97 @@ spec: name: HERMETIC type: string - default: "" - description: In case it is not empty, the prefetched content should be made available to the build. + description: In case it is not empty, the prefetched content should be made available + to the build. name: PREFETCH_INPUT type: string - default: "" - description: Delete image tag after specified time. Empty means to keep the image tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, respectively. + description: Delete image tag after specified time. Empty means to keep the image + tag. Time values could be something like 1h, 2d, 3w for hours, days, and weeks, + respectively. name: IMAGE_EXPIRES_AFTER type: string - - name: COMMIT_SHA + - default: "" description: The image is built from this commit. + name: COMMIT_SHA type: string - default: "" - - name: YUM_REPOS_D_SRC + - default: repos.d description: Path in the git repository in which yum repository files are stored - default: repos.d - - name: YUM_REPOS_D_FETCHED + name: YUM_REPOS_D_SRC + - default: fetched.repos.d description: Path in source workspace where dynamically-fetched repos are present - default: fetched.repos.d - - name: YUM_REPOS_D_TARGET - description: Target path on the container in which yum repository files should be made available - default: /etc/yum.repos.d - - name: TARGET_STAGE - description: Target stage in Dockerfile to build. If not specified, the Dockerfile is processed entirely to (and including) its last stage. + name: YUM_REPOS_D_FETCHED + - default: /etc/yum.repos.d + description: Target path on the container in which yum repository files should + be made available + name: YUM_REPOS_D_TARGET + - default: "" + description: Target stage in Dockerfile to build. If not specified, the Dockerfile + is processed entirely to (and including) its last stage. + name: TARGET_STAGE type: string - default: "" - - name: ENTITLEMENT_SECRET + - default: etc-pki-entitlement description: Name of secret which contains the entitlement certificates + name: ENTITLEMENT_SECRET type: string - default: "etc-pki-entitlement" - - name: ACTIVATION_KEY - default: activation-key + - default: activation-key description: Name of secret which contains subscription activation key + name: ACTIVATION_KEY type: string - - name: ADDITIONAL_SECRET - description: Name of a secret which will be made available to the build with 'buildah build --secret' at /run/secrets/$ADDITIONAL_SECRET + - default: does-not-exist + description: Name of a secret which will be made available to the build with 'buildah + build --secret' at /run/secrets/$ADDITIONAL_SECRET + name: ADDITIONAL_SECRET type: string - default: "does-not-exist" - - name: BUILD_ARGS + - default: [] description: Array of --build-arg values ("arg=value" strings) + name: BUILD_ARGS type: array - default: [] - - name: BUILD_ARGS_FILE + - default: "" description: Path to a file with build arguments, see https://www.mankier.com/1/buildah-build#--build-arg-file + name: BUILD_ARGS_FILE type: string - default: "" - - name: caTrustConfigMapName - type: string + - default: trusted-ca description: The name of the ConfigMap to read CA bundle data from. - default: trusted-ca - - name: caTrustConfigMapKey + name: caTrustConfigMapName + type: string + - default: ca-bundle.crt + description: The name of the key in the ConfigMap that contains the CA bundle + data. + name: caTrustConfigMapKey type: string - description: The name of the key in the ConfigMap that contains the CA bundle data. - default: ca-bundle.crt - - name: ADD_CAPABILITIES - description: Comma separated list of extra capabilities to add when running 'buildah build' + - default: "" + description: Comma separated list of extra capabilities to add when running 'buildah + build' + name: ADD_CAPABILITIES type: string - default: "" - - name: SQUASH - description: Squash all new and previous layers added as a part of this build, as per --squash + - default: "false" + description: Squash all new and previous layers added as a part of this build, + as per --squash + name: SQUASH type: string - default: "false" - - name: STORAGE_DRIVER + - default: vfs description: Storage driver to configure for buildah + name: STORAGE_DRIVER type: string - default: vfs - - name: SKIP_UNUSED_STAGES - description: Whether to skip stages in Containerfile that seem unused by subsequent stages + - default: "true" + description: Whether to skip stages in Containerfile that seem unused by subsequent + stages + name: SKIP_UNUSED_STAGES type: string - default: "true" - - name: LABELS + - default: [] description: Additional key=value labels that should be applied to the image + name: LABELS type: array - default: [] - - name: PRIVILEGED_NESTED + - default: "false" description: Whether to enable privileged mode + name: PRIVILEGED_NESTED type: string - default: "false" - - name: SKIP_SBOM_GENERATION - description: Skip SBOM-related operations. This will likely cause EC policies to fail if enabled + - default: "false" + description: Skip SBOM-related operations. This will likely cause EC policies + to fail if enabled + name: SKIP_SBOM_GENERATION type: string - default: "false" - results: - description: Digest of the image just built name: IMAGE_DIGEST @@ -122,20 +133,18 @@ spec: name: IMAGE_URL - description: Image reference of the built image name: IMAGE_REF - - name: SBOM_BLOB_URL - description: Reference of SBOM blob digest to enable digest-based verification from provenance + - description: Reference of SBOM blob digest to enable digest-based verification + from provenance + name: SBOM_BLOB_URL type: string stepTemplate: computeResources: limits: + cpu: "4" memory: 4Gi - cpu: '4' requests: + cpu: "1" memory: 1Gi - cpu: '1' - volumeMounts: - - mountPath: /shared - name: shared env: - name: BUILDAH_FORMAT value: oci @@ -179,28 +188,29 @@ spec: value: $(params.PRIVILEGED_NESTED) - name: SKIP_SBOM_GENERATION value: $(params.SKIP_SBOM_GENERATION) - + volumeMounts: + - mountPath: /shared + name: shared steps: - - image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c - name: build + - args: + - --build-args + - $(params.BUILD_ARGS[*]) + - --labels + - $(params.LABELS[*]) computeResources: limits: + cpu: "4" memory: 8Gi - cpu: '4' requests: + cpu: "1" memory: 2Gi - cpu: '1' env: - name: COMMIT_SHA value: $(params.COMMIT_SHA) - name: DOCKERFILE value: $(params.DOCKERFILE) - args: - - --build-args - - $(params.BUILD_ARGS[*]) - - --labels - - $(params.LABELS[*]) - + image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: build script: | #!/bin/bash set -euo pipefail @@ -482,40 +492,39 @@ spec: echo "$image $base_image_digest" | tee -a /shared/base_images_digests fi done - securityContext: capabilities: add: - - SETFCAP + - SETFCAP volumeMounts: - mountPath: /var/lib/containers name: varlibcontainers - - mountPath: "/entitlement" + - mountPath: /entitlement name: etc-pki-entitlement - mountPath: /activation-key name: activation-key - - mountPath: "/additional-secret" + - mountPath: /additional-secret name: additional-secret - - name: trusted-ca - mountPath: /mnt/trusted-ca + - mountPath: /mnt/trusted-ca + name: trusted-ca readOnly: true workingDir: $(workspaces.source.path) - - name: icm - image: quay.io/konflux-ci/icm-injection-scripts:latest@sha256:523689a26c74790be67d6036820abfb9aa10bec6efff98cc1c9b74bd2f99eeb1 + - image: quay.io/konflux-ci/icm-injection-scripts:latest@sha256:523689a26c74790be67d6036820abfb9aa10bec6efff98cc1c9b74bd2f99eeb1 + name: icm + script: | + #!/bin/bash + set -euo pipefail + /scripts/inject-icm.sh "$IMAGE" securityContext: capabilities: add: - - SETFCAP + - SETFCAP volumeMounts: - mountPath: /var/lib/containers name: varlibcontainers workingDir: $(workspaces.source.path) - script: | - #!/bin/bash - set -euo pipefail - /scripts/inject-icm.sh "$IMAGE" - - name: push - image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + - image: quay.io/konflux-ci/buildah-task:latest@sha256:b2d6c32d1e05e91920cd4475b2761d58bb7ee11ad5dff3ecb59831c7572b4d0c + name: push script: | #!/bin/bash set -e @@ -558,25 +567,20 @@ spec: echo -n "${IMAGE}@" cat "$(workspaces.source.path)/image-digest" } > "$(results.IMAGE_REF.path)" - securityContext: - runAsUser: 0 capabilities: add: - - SETFCAP + - SETFCAP + runAsUser: 0 volumeMounts: - mountPath: /var/lib/containers name: varlibcontainers - - name: trusted-ca - mountPath: /mnt/trusted-ca + - mountPath: /mnt/trusted-ca + name: trusted-ca readOnly: true workingDir: $(workspaces.source.path) - - - name: sbom-syft-generate - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c - # Respect Syft configuration if the user has it in the root of their repository - # (need to set the workdir, see https://github.com/anchore/syft/issues/2465) - workingDir: $(workspaces.source.path)/source + - image: registry.access.redhat.com/rh-syft-tech-preview/syft-rhel9:1.18.1@sha256:398f0ef395de137f05563d8de43e508bcb2e6810aa9d9adc638154b52c1a469c + name: sbom-syft-generate script: | if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then echo "Skipping SBOM generation" @@ -591,16 +595,16 @@ spec: name: varlibcontainers - mountPath: /shared name: shared - - - name: prepare-sboms - image: quay.io/konflux-ci/sbom-utility-scripts@sha256:7f4ce55033077f37eda61c67b4289949471c36d23ae0cc66fd4113e615b3ef93 - computeResources: + workingDir: $(workspaces.source.path)/source + - computeResources: limits: - memory: 512Mi cpu: 200m + memory: 512Mi requests: - memory: 256Mi cpu: 100m + memory: 256Mi + image: quay.io/konflux-ci/sbom-utility-scripts@sha256:7f4ce55033077f37eda61c67b4289949471c36d23ae0cc66fd4113e615b3ef93 + name: prepare-sboms script: | if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then echo "Skipping SBOM generation" @@ -632,12 +636,18 @@ spec: --output-file /tmp/sbom-cyclonedx.tmp.json mv /tmp/sbom-cyclonedx.tmp.json sbom-cyclonedx.json - workingDir: $(workspaces.source.path) securityContext: runAsUser: 0 - - - name: upload-sbom + workingDir: $(workspaces.source.path) + - computeResources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi image: quay.io/konflux-ci/appstudio-utils:48c311af02858e2422d6229600e9959e496ddef1@sha256:91ddd999271f65d8ec8487b10f3dd378f81aa894e11b9af4d10639fd52bba7e8 + name: upload-sbom script: | #!/bin/bash if [ "${SKIP_SBOM_GENERATION}" = "true" ]; then @@ -659,44 +669,35 @@ spec: sbom_digest="$(sha256sum sbom-cyclonedx.json | cut -d' ' -f1)" # The SBOM_BLOB_URL is created by `cosign attach sbom`. echo -n "${sbom_repo}@sha256:${sbom_digest}" | tee "$(results.SBOM_BLOB_URL.path)" - - computeResources: - limits: - memory: 512Mi - cpu: 200m - requests: - memory: 256Mi - cpu: 100m volumeMounts: - - name: trusted-ca - mountPath: /mnt/trusted-ca + - mountPath: /mnt/trusted-ca + name: trusted-ca readOnly: true workingDir: $(workspaces.source.path) - volumes: - - name: varlibcontainers - emptyDir: {} - - name: shared - emptyDir: {} + - emptyDir: {} + name: varlibcontainers + - emptyDir: {} + name: shared - name: etc-pki-entitlement secret: - secretName: $(params.ENTITLEMENT_SECRET) optional: true + secretName: $(params.ENTITLEMENT_SECRET) - name: activation-key secret: optional: true secretName: $(params.ACTIVATION_KEY) - name: additional-secret secret: - secretName: $(params.ADDITIONAL_SECRET) optional: true - - name: trusted-ca - configMap: - name: $(params.caTrustConfigMapName) + secretName: $(params.ADDITIONAL_SECRET) + - configMap: items: - - key: $(params.caTrustConfigMapKey) - path: ca-bundle.crt + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) optional: true + name: trusted-ca workspaces: - - name: source - description: Workspace containing the source code to build. + - description: Workspace containing the source code to build. + name: source diff --git a/task/fbc-related-image-check/0.1/fbc-related-image-check.yaml b/task/fbc-related-image-check/0.1/fbc-related-image-check.yaml index 0dba6be564..d98c7c80bf 100644 --- a/task/fbc-related-image-check/0.1/fbc-related-image-check.yaml +++ b/task/fbc-related-image-check/0.1/fbc-related-image-check.yaml @@ -1,74 +1,75 @@ apiVersion: tekton.dev/v1 kind: Task metadata: - labels: - app.kubernetes.io/version: "0.1" annotations: - tekton.dev/pipelines.minVersion: "0.12.1" - tekton.dev/tags: "konflux" build.appstudio.redhat.com/expires-on: "2025-01-31T00:00:00Z" + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: konflux + labels: + app.kubernetes.io/version: "0.1" name: fbc-related-image-check spec: - description: >- - Checks the validity of all the images referenced in the file-based catalog (FBC) to inspect manifest content using Skopeo. + description: Checks the validity of all the images referenced in the file-based + catalog (FBC) to inspect manifest content using Skopeo. results: - - name: TEST_OUTPUT - description: Tekton task test output. - workspaces: - - name: workspace + - description: Tekton task test output. + name: TEST_OUTPUT steps: - - name: check-related-images - image: quay.io/konflux-ci/konflux-test:v1.4.11@sha256:540f795828852c90ec8f7d1b7b5e66e88700dc3dfe45d9cad7e2b8f64217bea8 - # per https://kubernetes.io/docs/concepts/containers/images/#imagepullpolicy-defaulting - # the cluster will set imagePullPolicy to IfNotPresent - workingDir: $(workspaces.workspace.path)/hacbs/$(context.task.name) - computeResources: - limits: - memory: 4Gi - requests: - memory: 512Mi - cpu: 10m - script: | - #!/usr/bin/env bash - set -euo pipefail - source /utils.sh - trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT + - computeResources: + limits: + memory: 4Gi + requests: + cpu: 10m + memory: 512Mi + image: quay.io/konflux-ci/konflux-test:v1.4.11@sha256:540f795828852c90ec8f7d1b7b5e66e88700dc3dfe45d9cad7e2b8f64217bea8 + name: check-related-images + script: | + #!/usr/bin/env bash + set -euo pipefail + # shellcheck source=/dev/null + source /utils.sh + trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT - FAILEDIMAGES="" - catalog="$(opm render $(workspaces.workspace.path)/hacbs/fbc-validation/confdir/)" + FAILEDIMAGES="" + # shellcheck disable=SC2046 + catalog="$(opm render $(workspaces.workspace.path)/hacbs/fbc-validation/confdir/)" - # OPM generates catalog file in a way that yaml file could contain stream of JSON objects - # thats why we need jq in for this situation, because yq can't parse this file - # however there is also posibility that catalog.yaml has yaml data in it + # OPM generates catalog file in a way that yaml file could contain stream of JSON objects + # thats why we need jq in for this situation, because yq can't parse this file + # however there is also posibility that catalog.yaml has yaml data in it - status=0 - relImgs="$(jq -r '.relatedImages[]?.image' <<< ${catalog})" || status=$? - if [ $status -ne 0 ]; then - echo "Could not get related images. Make sure catalog.yaml exists in FBC fragment image and it is valid .yaml or .json format." - note="Task $(context.task.name) failed: Could not fetch related images. Make sure you have catalog.yaml or catalog.json formatted correctly in your file-based catalog (FBC) fragment image." - TEST_OUTPUT=$(make_result_json -r FAILURE -f 1 -t "$note") - echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" - exit 0 - fi + status=0 + # shellcheck disable=SC2086 + relImgs="$(jq -r '.relatedImages[]?.image' <<< ${catalog})" || status=$? + if [ $status -ne 0 ]; then + echo "Could not get related images. Make sure catalog.yaml exists in FBC fragment image and it is valid .yaml or .json format." + note="Task $(context.task.name) failed: Could not fetch related images. Make sure you have catalog.yaml or catalog.json formatted correctly in your file-based catalog (FBC) fragment image." + TEST_OUTPUT=$(make_result_json -r FAILURE -f 1 -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi - echo -e "These are related images:\n$relImgs." - # cycle through those related images and show outputs - for i in ${relImgs// /} - do - if ! skopeo inspect --no-tags "docker://${i}"; then - echo "Skopeo inspect failed on related image: $i." - FAILEDIMAGES+="$i, " - fi - done - if [ -z "$FAILEDIMAGES" ]; then - note="Task $(context.task.name) succeeded: For details, check Tekton task logs." - TEST_OUTPUT=$(make_result_json -r SUCCESS -s 1 -t "$note") - echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" - exit 0 - else - echo "These images failed inspection: $FAILEDIMAGES." - note="Task $(context.task.name) failed: Command skopeo inspect could not inspect images. For details, check Tekton task log." - TEST_OUTPUT=$(make_result_json -r FAILURE -f 1 -t "$note") - echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" - exit 0 + echo -e "These are related images:\n$relImgs." + # cycle through those related images and show outputs + for i in ${relImgs// /} + do + if ! skopeo inspect --no-tags "docker://${i}"; then + echo "Skopeo inspect failed on related image: $i." + FAILEDIMAGES+="$i, " fi + done + if [ -z "$FAILEDIMAGES" ]; then + note="Task $(context.task.name) succeeded: For details, check Tekton task logs." + TEST_OUTPUT=$(make_result_json -r SUCCESS -s 1 -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + else + echo "These images failed inspection: $FAILEDIMAGES." + note="Task $(context.task.name) failed: Command skopeo inspect could not inspect images. For details, check Tekton task log." + TEST_OUTPUT=$(make_result_json -r FAILURE -f 1 -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi + workingDir: $(workspaces.workspace.path)/hacbs/$(context.task.name) + workspaces: + - name: workspace diff --git a/task/fbc-related-image-check/0.2/fbc-related-image-check.yaml b/task/fbc-related-image-check/0.2/fbc-related-image-check.yaml new file mode 100644 index 0000000000..95f8ebec46 --- /dev/null +++ b/task/fbc-related-image-check/0.2/fbc-related-image-check.yaml @@ -0,0 +1,75 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2025-01-31T00:00:00Z" + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: konflux + labels: + app.kubernetes.io/version: "0.2" + name: fbc-related-image-check +spec: + description: Checks the validity of all the images referenced in the file-based + catalog (FBC) to inspect manifest content using Skopeo. + results: + - description: Tekton task test output. + name: TEST_OUTPUT + steps: + - computeResources: + limits: + memory: 4Gi + requests: + cpu: 10m + memory: 512Mi + image: quay.io/konflux-ci/konflux-test:v1.4.11@sha256:540f795828852c90ec8f7d1b7b5e66e88700dc3dfe45d9cad7e2b8f64217bea8 + name: check-related-images + script: | + #!/usr/bin/env bash + set -euo pipefail + # shellcheck source=/dev/null + source /utils.sh + trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT + + FAILEDIMAGES="" + # shellcheck disable=SC2046 + catalog="$(opm render $(workspaces.workspace.path)/hacbs/fbc-validation/confdir/)" + + # OPM generates catalog file in a way that yaml file could contain stream of JSON objects + # thats why we need jq in for this situation, because yq can't parse this file + # however there is also posibility that catalog.yaml has yaml data in it + + status=0 + # shellcheck disable=SC2086 + relImgs="$(jq -r '.relatedImages[]?.image' <<< ${catalog})" || status=$? + if [ $status -ne 0 ]; then + echo "Could not get related images. Make sure catalog.yaml exists in FBC fragment image and it is valid .yaml or .json format." + note="Task $(context.task.name) failed: Could not fetch related images. Make sure you have catalog.yaml or catalog.json formatted correctly in your file-based catalog (FBC) fragment image." + TEST_OUTPUT=$(make_result_json -r FAILURE -f 1 -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi + + echo -e "These are related images:\n$relImgs." + # cycle through those related images and show outputs + for i in ${relImgs// /} + do + if ! skopeo inspect --no-tags "docker://${i}"; then + echo "Skopeo inspect failed on related image: $i." + FAILEDIMAGES+="$i, " + fi + done + if [ -z "$FAILEDIMAGES" ]; then + note="Task $(context.task.name) succeeded: For details, check Tekton task logs." + TEST_OUTPUT=$(make_result_json -r SUCCESS -s 1 -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + else + echo "These images failed inspection: $FAILEDIMAGES." + note="Task $(context.task.name) failed: Command skopeo inspect could not inspect images. For details, check Tekton task log." + TEST_OUTPUT=$(make_result_json -r FAILURE -f 1 -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi + workingDir: $(workspaces.workspace.path)/hacbs/$(context.task.name) + workspaces: + - name: workspace diff --git a/task/fbc-validation/0.1/fbc-validation.yaml b/task/fbc-validation/0.1/fbc-validation.yaml index 7a5f9f05b7..a5d35431b2 100644 --- a/task/fbc-validation/0.1/fbc-validation.yaml +++ b/task/fbc-validation/0.1/fbc-validation.yaml @@ -1,280 +1,282 @@ apiVersion: tekton.dev/v1 kind: Task metadata: - labels: - app.kubernetes.io/version: "0.1" annotations: - tekton.dev/pipelines.minVersion: "0.12.1" - tekton.dev/tags: "konflux" build.appstudio.redhat.com/expires-on: "2025-01-31T00:00:00Z" + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: konflux + labels: + app.kubernetes.io/version: "0.1" name: fbc-validation spec: - description: >- - Ensures file-based catalog (FBC) components are uniquely linted for proper construction as part of build pipeline. + description: Ensures file-based catalog (FBC) components are uniquely linted for + proper construction as part of build pipeline. params: + - description: Fully qualified image name. + name: IMAGE_URL + - description: Image digest. + name: IMAGE_DIGEST + - description: Fully qualified base image name. + name: BASE_IMAGE + results: + - description: Tekton task test output. + name: TEST_OUTPUT + steps: + - computeResources: + limits: + memory: 4Gi + requests: + cpu: 10m + memory: 512Mi + env: - name: IMAGE_URL - description: Fully qualified image name. + value: $(params.IMAGE_URL) - name: IMAGE_DIGEST - description: Image digest. + value: $(params.IMAGE_DIGEST) - name: BASE_IMAGE - description: Fully qualified base image name. - results: - - name: TEST_OUTPUT - description: Tekton task test output. - workspaces: - - name: workspace - steps: - - name: extract-and-check-binaries - image: quay.io/konflux-ci/konflux-test:v1.4.11@sha256:540f795828852c90ec8f7d1b7b5e66e88700dc3dfe45d9cad7e2b8f64217bea8 - # per https://kubernetes.io/docs/concepts/containers/images/#imagepullpolicy-defaulting - # the cluster will set imagePullPolicy to IfNotPresent - workingDir: $(workspaces.workspace.path)/hacbs/$(context.task.name) - env: - - name: IMAGE_URL - value: $(params.IMAGE_URL) - - name: IMAGE_DIGEST - value: $(params.IMAGE_DIGEST) - - name: BASE_IMAGE - value: "$(params.BASE_IMAGE)" - securityContext: - runAsUser: 0 - capabilities: - add: - - SETFCAP - computeResources: - limits: - memory: 4Gi - requests: - memory: 512Mi - cpu: 10m - script: | - #!/usr/bin/env bash - set -euo pipefail - source /utils.sh - trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT + value: $(params.BASE_IMAGE) + image: quay.io/konflux-ci/konflux-test:v1.4.11@sha256:540f795828852c90ec8f7d1b7b5e66e88700dc3dfe45d9cad7e2b8f64217bea8 + name: extract-and-check-binaries + script: | + #!/usr/bin/env bash + set -euo pipefail + # shellcheck source=/dev/null + source /utils.sh + trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT - declare -a ALLOWED_BASE_IMAGES=( - "registry.redhat.io/openshift4/ose-operator-registry" - "registry.redhat.io/openshift4/ose-operator-registry-rhel9" - "brew.registry.redhat.io/rh-osbs/openshift-ose-operator-registry-rhel9" - ) + declare -a ALLOWED_BASE_IMAGES=( + "registry.redhat.io/openshift4/ose-operator-registry" + "registry.redhat.io/openshift4/ose-operator-registry-rhel9" + "brew.registry.redhat.io/rh-osbs/openshift-ose-operator-registry-rhel9" + ) - ### FBC base image check - if [ -z "${BASE_IMAGE}" ]; then - echo "Base image is unknown. The file-based catalog must have base image defined. Check inspect-image task log." - note="Task $(context.task.name) failed: The file-based catalog must have base image defined. For details, check Tekton task result TEST_OUTPUT in task inspect-image." - TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") - echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" - exit 0 - fi + ### FBC base image check + if [ -z "${BASE_IMAGE}" ]; then + echo "Base image is unknown. The file-based catalog must have base image defined. Check inspect-image task log." + note="Task $(context.task.name) failed: The file-based catalog must have base image defined. For details, check Tekton task result TEST_OUTPUT in task inspect-image." + TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi - IMAGE_WITHOUT_TAG=$(echo "${BASE_IMAGE}" | sed "s/:.*$//" | sed "s/@.*$//") - - allowed=false - for value in "${ALLOWED_BASE_IMAGES[@]}" - do - if [[ "${IMAGE_WITHOUT_TAG}" == "${value}" ]]; then - allowed=true - break - fi - done + IMAGE_WITHOUT_TAG=$(echo "${BASE_IMAGE}" | sed "s/:.*$//" | sed "s/@.*$//") - if [[ "${allowed}" == false ]]; then - echo "Base image ${BASE_IMAGE} is not allowed for the file based catalog image. Allowed images: ${ALLOWED_BASE_IMAGES}" - note="Task $(context.task.name) failed: Base image ${BASE_IMAGE} is not allowed for the file based catalog image. For details, check Tekton task logs" - TEST_OUTPUT=$(make_result_json -r FAILURE -f 1 -t "$note") - echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" - exit 0 + allowed=false + for value in "${ALLOWED_BASE_IMAGES[@]}" + do + if [[ "${IMAGE_WITHOUT_TAG}" == "${value}" ]]; then + allowed=true + break fi + done - ### Try to extract binaries with configs > check binaries functionality > check opm validate ### - if [ ! -s ../inspect-image/image_inspect.json ]; then - echo "File $(workspaces.workspace.path)/hacbs/inspect-image/image_inspect.json did not generate correctly. Check inspect-image task log." - note="Task $(context.task.name) failed: $(workspaces.workspace.path)/hacbs/inspect-image/image_inspect.json did not generate correctly. For details, check Tekton task result TEST_OUTPUT in task inspect-image." - TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") - echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" - exit 0 - fi + if [[ "${allowed}" == false ]]; then + # shellcheck disable=SC2128 + echo "Base image ${BASE_IMAGE} is not allowed for the file based catalog image. Allowed images: ${ALLOWED_BASE_IMAGES}" + note="Task $(context.task.name) failed: Base image ${BASE_IMAGE} is not allowed for the file based catalog image. For details, check Tekton task logs" + TEST_OUTPUT=$(make_result_json -r FAILURE -f 1 -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi - if [ ! -s ../inspect-image/raw_image_inspect.json ]; then - echo "File $(workspaces.workspace.path)/hacbs/inspect-image/raw_image_inspect.json did not generate correctly. Check inspect-image task log." - note="Task $(context.task.name) failed: $(workspaces.workspace.path)/hacbs/inspect-image/raw_image_inspect.json did not generate correctly. For details, check Tekton task result TEST_OUTPUT in task inspect-image." - TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") - echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" - exit 0 - fi + ### Try to extract binaries with configs > check binaries functionality > check opm validate ### + if [ ! -s ../inspect-image/image_inspect.json ]; then + echo "File $(workspaces.workspace.path)/hacbs/inspect-image/image_inspect.json did not generate correctly. Check inspect-image task log." + note="Task $(context.task.name) failed: $(workspaces.workspace.path)/hacbs/inspect-image/image_inspect.json did not generate correctly. For details, check Tekton task result TEST_OUTPUT in task inspect-image." + TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi + + if [ ! -s ../inspect-image/raw_image_inspect.json ]; then + echo "File $(workspaces.workspace.path)/hacbs/inspect-image/raw_image_inspect.json did not generate correctly. Check inspect-image task log." + note="Task $(context.task.name) failed: $(workspaces.workspace.path)/hacbs/inspect-image/raw_image_inspect.json did not generate correctly. For details, check Tekton task result TEST_OUTPUT in task inspect-image." + TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi - echo "Getting base image for source image ${IMAGE_URL}." - base_image_name="$(jq -r ".annotations.\"org.opencontainers.image.base.name\"" ../inspect-image/raw_image_inspect.json)" || status=$? + echo "Getting base image for source image ${IMAGE_URL}." + base_image_name="$(jq -r ".annotations.\"org.opencontainers.image.base.name\"" ../inspect-image/raw_image_inspect.json)" || status=$? + if [ "$base_image_name" == 'null' ]; then + echo "Could not get annotations from inspect-image/raw_image_inspect.json. Make sure file exists and it contains this annotation: org.opencontainers.image.base.name" + echo "Try to get base image from label..." + base_image_name="$(jq -r ".Labels.\"org.opencontainers.image.base.name\"" ../inspect-image/image_inspect.json)" || status=$? if [ "$base_image_name" == 'null' ]; then - echo "Could not get annotations from inspect-image/raw_image_inspect.json. Make sure file exists and it contains this annotation: org.opencontainers.image.base.name" - echo "Try to get base image from label..." - base_image_name="$(jq -r ".Labels.\"org.opencontainers.image.base.name\"" ../inspect-image/image_inspect.json)" || status=$? - if [ "$base_image_name" == 'null' ]; then - echo "Cannot get base image info from Labels. For details, check source image ../inspect-image/image_inspect.json." - TEST_OUTPUT="$(make_result_json -r ERROR)" - echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" - exit 0 - fi - fi - if [ -z "$base_image_name" ]; then - echo "Source image ${IMAGE_URL} is built from scratch, so there is no base image." + echo "Cannot get base image info from Labels. For details, check source image ../inspect-image/image_inspect.json." TEST_OUTPUT="$(make_result_json -r ERROR)" echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" exit 0 fi + fi + if [ -z "$base_image_name" ]; then + echo "Source image ${IMAGE_URL} is built from scratch, so there is no base image." + TEST_OUTPUT="$(make_result_json -r ERROR)" + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi - status=0 - conffolder=$(jq -r '.Labels ."operators.operatorframework.io.index.configs.v1"' ../inspect-image/image_inspect.json) || status=$? - if [ $status -ne 0 ]; then - echo "Could not get labels from inspect-image/image_inspect.json. Make sure file exists and it contains this label: operators.operatorframework.io.index.configs.v1." - TEST_OUTPUT="$(make_result_json -r ERROR)" - echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" - exit 0 - fi - mkdir -p /tmp/image-content confdir - pushd /tmp/image-content - image_with_digest="${IMAGE_URL}@${IMAGE_DIGEST}" + status=0 + conffolder=$(jq -r '.Labels ."operators.operatorframework.io.index.configs.v1"' ../inspect-image/image_inspect.json) || status=$? + if [ $status -ne 0 ]; then + echo "Could not get labels from inspect-image/image_inspect.json. Make sure file exists and it contains this label: operators.operatorframework.io.index.configs.v1." + TEST_OUTPUT="$(make_result_json -r ERROR)" + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi + mkdir -p /tmp/image-content confdir + pushd /tmp/image-content + image_with_digest="${IMAGE_URL}@${IMAGE_DIGEST}" - if ! oc image extract --registry-config ~/.docker/config.json "${image_with_digest}" ; then - echo "Unable to extract or validate extracted binaries." - note="Task $(context.task.name) failed: Failed to extract image with oc extract command, so it cannot validate extracted binaries. For details, check Tekton task log." - ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") - echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" - popd - exit 0 - fi + if ! oc image extract --registry-config ~/.docker/config.json "${image_with_digest}" ; then + echo "Unable to extract or validate extracted binaries." + note="Task $(context.task.name) failed: Failed to extract image with oc extract command, so it cannot validate extracted binaries. For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + popd + exit 0 + fi - if [ -z "$(ls -A .$conffolder)" ]; then - echo "$conffolder is missing catalog file." - TEST_OUTPUT="$(make_result_json -r ERROR)" - echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" - popd - exit 0 - fi - # copy content of conffolder to confdir - will be used in next task - related image check - cp -r .$conffolder/* $(workspaces.workspace.path)/hacbs/$(context.task.name)/confdir + # shellcheck disable=SC2086 + if [ -z "$(ls -A .$conffolder)" ]; then + echo "$conffolder is missing catalog file." + TEST_OUTPUT="$(make_result_json -r ERROR)" + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + popd + exit 0 + fi + # copy content of conffolder to confdir - will be used in next task - related image check + # shellcheck disable=SC2086 + cp -r .$conffolder/* "$(workspaces.workspace.path)/hacbs/$(context.task.name)/confdir" + + EXTRACT_DIR="/extracted_base_img" + mkdir "${EXTRACT_DIR}" + if ! oc image extract "${BASE_IMAGE}" --path /:"${EXTRACT_DIR}"; then + echo "Unable to extract opm binary" + note="Task $(context.task.name) failed: Failed to extract base image with oc extract command, so it cannot validate extracted binaries. For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + exit 0 + fi - EXTRACT_DIR="/extracted_base_img" - mkdir "${EXTRACT_DIR}" - if ! oc image extract ${BASE_IMAGE} --path /:"${EXTRACT_DIR}"; then - echo "Unable to extract opm binary" - note="Task $(context.task.name) failed: Failed to extract base image with oc extract command, so it cannot validate extracted binaries. For details, check Tekton task log." + OPM_BINARIES="$(find "${EXTRACT_DIR}" -type f -name opm)" + BINARIES_COUNT=$(wc -l <<< "${OPM_BINARIES}") + if [[ $BINARIES_COUNT -ne "1" ]]; then + note="Task $(context.task.name) failed: Expected exactly one opm binary in base image. For details, check Tekton task log" ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + echo "found $BINARIES_COUNT opm binaries:" + echo "${OPM_BINARIES}" exit 0 - fi + fi + OPM_BINARY=$(echo "${OPM_BINARIES}" | head -n 1) + echo "OPM_BINARY: '${OPM_BINARY}'" + chmod 775 "$OPM_BINARY" - OPM_BINARIES="$(find "${EXTRACT_DIR}" -type f -name opm)" - BINARIES_COUNT=$(wc -l <<< "${OPM_BINARIES}") - if [[ $BINARIES_COUNT -ne "1" ]]; then - note="Task $(context.task.name) failed: Expected exactly one opm binary in base image. For details, check Tekton task log" - ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") - echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" - echo "found $BINARIES_COUNT opm binaries:" - echo "${OPM_BINARIES}" - exit 0 - fi - OPM_BINARY=$(echo "${OPM_BINARIES}" | head -n 1) - echo "OPM_BINARY: '${OPM_BINARY}'" - chmod 775 "$OPM_BINARY" + # We have 6 total checks + check_num=6 + failure_num=0 + TESTPASSED=true - # We have 6 total checks - check_num=6 - failure_num=0 - TESTPASSED=true + if [[ ! $(find . -name "grpc_health_probe") ]]; then + echo "!FAILURE! - grpc_health_probe binary presence check failed." + failure_num=$((failure_num + 1)) + TESTPASSED=false + fi - if [[ ! $(find . -name "grpc_health_probe") ]]; then - echo "!FAILURE! - grpc_health_probe binary presence check failed." - failure_num=$((failure_num + 1)) - TESTPASSED=false - fi + if ! ${OPM_BINARY} validate ."${conffolder}"; then + echo "!FAILURE! - opm validate check failed." + failure_num=$((failure_num + 1)) + TESTPASSED=false + fi - if ! ${OPM_BINARY} validate ."${conffolder}"; then - echo "!FAILURE! - opm validate check failed." - failure_num=$((failure_num + 1)) - TESTPASSED=false - fi + OPM_RENDERED_CATALOG=/tmp/catalog.json + ${OPM_BINARY} render ."${conffolder}" > ${OPM_RENDERED_CATALOG} + if [ ! -f ${OPM_RENDERED_CATALOG} ]; then + echo "!FAILURE! - unable to render the fragment FBC." + failure_num=$((failure_num + 1)) + TESTPASSED=false + fi - OPM_RENDERED_CATALOG=/tmp/catalog.json - ${OPM_BINARY} render ."${conffolder}" > ${OPM_RENDERED_CATALOG} - if [ ! -f ${OPM_RENDERED_CATALOG} ]; then - echo "!FAILURE! - unable to render the fragment FBC." - failure_num=$((failure_num + 1)) - TESTPASSED=false - fi + if jq -en 'reduce (inputs | select(.schema == "olm.package")) as $obj (0; .+1) < 1' ${OPM_RENDERED_CATALOG}; then + echo "!FAILURE! - There are no olm package entries defined in this FBC fragment." + failure_num=$((failure_num + 1)) + TESTPASSED=false + fi - if jq -en 'reduce (inputs | select(.schema == "olm.package")) as $obj (0; .+1) < 1' ${OPM_RENDERED_CATALOG}; then - echo "!FAILURE! - There are no olm package entries defined in this FBC fragment." - failure_num=$((failure_num + 1)) - TESTPASSED=false - fi + # examines the base_image_name tag to derive the target OCP version + # assumes this is in the form + # image-path:[v]major-digits.minor-digits[@sha...] + OCP_VER_FROM_BASE=$(echo "${base_image_name}" | sed -e "s/@.*$//" -e "s/^.*://") # strips hash first due to greedy match + # extracts major digits and filters out any leading alphabetic characters, for e.g. 'v4' --> '4' + OCP_VER_MAJOR=$(echo "${OCP_VER_FROM_BASE}" | cut -d '.' -f 1 | sed "s/^[a-zA-Z]*//") + OCP_VER_MINOR=$(echo "${OCP_VER_FROM_BASE}" | cut -d '.' -f 2) - # examines the base_image_name tag to derive the target OCP version - # assumes this is in the form - # image-path:[v]major-digits.minor-digits[@sha...] - OCP_VER_FROM_BASE=$(echo "${base_image_name}" | sed -e "s/@.*$//" -e "s/^.*://") # strips hash first due to greedy match - # extracts major digits and filters out any leading alphabetic characters, for e.g. 'v4' --> '4' - OCP_VER_MAJOR=$(echo "${OCP_VER_FROM_BASE}" | cut -d '.' -f 1 | sed "s/^[a-zA-Z]*//") - OCP_VER_MINOR=$(echo "${OCP_VER_FROM_BASE}" | cut -d '.' -f 2) + RUN_OCP_VERSION_VALIDATION="false" + digits_regex='^[0-9]*$' + if [[ ${OCP_VER_MAJOR} =~ $digits_regex ]] && [[ ${OCP_VER_MINOR} =~ $digits_regex ]] ; then + RUN_OCP_VERSION_VALIDATION="true" + fi - RUN_OCP_VERSION_VALIDATION="false" - digits_regex='^[0-9]*$' - if [[ ${OCP_VER_MAJOR} =~ $digits_regex ]] && [[ ${OCP_VER_MINOR} =~ $digits_regex ]] ; then - RUN_OCP_VERSION_VALIDATION="true" - fi + if [ "${RUN_OCP_VERSION_VALIDATION}" == "false" ] ; then + echo "!WARNING! - unable to assess bundle metadata alignment with OCP version because we cannot extract version info from base_image_name: ${base_image_name}" + else + OCP_BUNDLE_METADATA_THRESHOLD_MAJOR=4 + OCP_BUNDLE_METADATA_THRESHOLD_MINOR=17 + OCP_BUNDLE_METADATA_FORMAT="olm.bundle.object" - if [ "${RUN_OCP_VERSION_VALIDATION}" == "false" ] ; then - echo "!WARNING! - unable to assess bundle metadata alignment with OCP version because we cannot extract version info from base_image_name: ${base_image_name}" - else - OCP_BUNDLE_METADATA_THRESHOLD_MAJOR=4 - OCP_BUNDLE_METADATA_THRESHOLD_MINOR=17 - OCP_BUNDLE_METADATA_FORMAT="olm.bundle.object" + if [[ "${OCP_VER_MAJOR}" -ge "${OCP_BUNDLE_METADATA_THRESHOLD_MAJOR}" ]] && [[ "${OCP_VER_MINOR}" -ge "${OCP_BUNDLE_METADATA_THRESHOLD_MINOR}" ]]; then + OCP_BUNDLE_METADATA_FORMAT="olm.csv.metadata" + fi - if [[ "${OCP_VER_MAJOR}" -ge "${OCP_BUNDLE_METADATA_THRESHOLD_MAJOR}" ]] && [[ "${OCP_VER_MINOR}" -ge "${OCP_BUNDLE_METADATA_THRESHOLD_MINOR}" ]]; then - OCP_BUNDLE_METADATA_FORMAT="olm.csv.metadata" + # enforce the presence of either olm.csv.metadata or olm.bundle.object based on OCP version + if [[ "${OCP_BUNDLE_METADATA_FORMAT}" = "olm.csv.metadata" ]]; then + if ! jq -en 'reduce( inputs | select(.schema == "olm.bundle" and .properties[].type == "olm.bundle.object")) as $_ (0;.+1) == 0' ${OPM_RENDERED_CATALOG}; then + echo "!FAILURE! - olm.bundle.object bundle properties are not permitted in a FBC fragment for OCP version ${OCP_VER_MAJOR}.${OCP_VER_MINOR}. Fragments must move to olm.csv.metadata bundle metadata." + failure_num=$((failure_num + 1)) + TESTPASSED=false fi - - # enforce the presence of either olm.csv.metadata or olm.bundle.object based on OCP version - if [[ "${OCP_BUNDLE_METADATA_FORMAT}" = "olm.csv.metadata" ]]; then - if ! jq -en 'reduce( inputs | select(.schema == "olm.bundle" and .properties[].type == "olm.bundle.object")) as $_ (0;.+1) == 0' ${OPM_RENDERED_CATALOG}; then - echo "!FAILURE! - olm.bundle.object bundle properties are not permitted in a FBC fragment for OCP version ${OCP_VER_MAJOR}.${OCP_VER_MINOR}. Fragments must move to olm.csv.metadata bundle metadata." - failure_num=$((failure_num + 1)) - TESTPASSED=false - fi - else - if ! jq -en 'reduce( inputs | select(.schema == "olm.bundle" and .properties[].type == "olm.csv.metadata")) as $_ (0;.+1) == 0' ${OPM_RENDERED_CATALOG}; then - echo "!FAILURE! - olm.csv.metadata bundle properties are not permitted in a FBC fragment for OCP version ${OCP_VER_MAJOR}.${OCP_VER_MINOR}. Fragments must only use olm.bundle.object bundle metadata." - failure_num=$((failure_num + 1)) - TESTPASSED=false - fi + else + if ! jq -en 'reduce( inputs | select(.schema == "olm.bundle" and .properties[].type == "olm.csv.metadata")) as $_ (0;.+1) == 0' ${OPM_RENDERED_CATALOG}; then + echo "!FAILURE! - olm.csv.metadata bundle properties are not permitted in a FBC fragment for OCP version ${OCP_VER_MAJOR}.${OCP_VER_MINOR}. Fragments must only use olm.bundle.object bundle metadata." + failure_num=$((failure_num + 1)) + TESTPASSED=false fi + fi - # enforce that each bundle has the OCP-version-appropriate bundle metadata. - BUNDLE_COUNT=$(jq -en 'def count(stream): reduce stream as $i (0; .+1); count(inputs|select(.schema=="olm.bundle"))' ${OPM_RENDERED_CATALOG}) - BUNDLE_BO_COUNT=$(jq -en 'def count(stream): reduce stream as $i (0; .+1); count(inputs|select(.schema == "olm.bundle" and .properties[].type == "olm.bundle.object"))' ${OPM_RENDERED_CATALOG}) - BUNDLE_CM_COUNT=$(jq -en 'def count(stream): reduce stream as $i (0; .+1); count(inputs|select(.schema == "olm.bundle" and .properties[].type == "olm.csv.metadata"))' ${OPM_RENDERED_CATALOG}) + # enforce that each bundle has the OCP-version-appropriate bundle metadata. + BUNDLE_COUNT=$(jq -en 'def count(stream): reduce stream as $i (0; .+1); count(inputs|select(.schema=="olm.bundle"))' ${OPM_RENDERED_CATALOG}) + BUNDLE_BO_COUNT=$(jq -en 'def count(stream): reduce stream as $i (0; .+1); count(inputs|select(.schema == "olm.bundle" and .properties[].type == "olm.bundle.object"))' ${OPM_RENDERED_CATALOG}) + BUNDLE_CM_COUNT=$(jq -en 'def count(stream): reduce stream as $i (0; .+1); count(inputs|select(.schema == "olm.bundle" and .properties[].type == "olm.csv.metadata"))' ${OPM_RENDERED_CATALOG}) - if [[ "${OCP_BUNDLE_METADATA_FORMAT}" = "olm.csv.metadata" ]]; then - if [[ "${BUNDLE_COUNT}" -ne "${BUNDLE_CM_COUNT}" ]]; then - echo "!FAILURE! - every olm.bundle object in the fragment must have a corresponding olm.csv.metadata bundle property" - failure_num=$((failure_num + 1)) - TESTPASSED=false - fi - else - if [[ "${BUNDLE_BO_COUNT}" -lt "${BUNDLE_COUNT}" ]]; then - echo "!FAILURE! - every olm.bundle object in the fragment must have at least one olm.bundle.object bundle property" - failure_num=$((failure_num + 1)) - TESTPASSED=false - fi + if [[ "${OCP_BUNDLE_METADATA_FORMAT}" = "olm.csv.metadata" ]]; then + if [[ "${BUNDLE_COUNT}" -ne "${BUNDLE_CM_COUNT}" ]]; then + echo "!FAILURE! - every olm.bundle object in the fragment must have a corresponding olm.csv.metadata bundle property" + failure_num=$((failure_num + 1)) + TESTPASSED=false fi - fi - - note="Task $(context.task.name) completed: Check result for task result." - if [ $TESTPASSED == false ]; then - ERROR_OUTPUT=$(make_result_json -r FAILURE -f $failure_num -s $((check_num - failure_num)) -t "$note") - echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" else - TEST_OUTPUT=$(make_result_json -r SUCCESS -s $check_num -t "$note") - echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + if [[ "${BUNDLE_BO_COUNT}" -lt "${BUNDLE_COUNT}" ]]; then + echo "!FAILURE! - every olm.bundle object in the fragment must have at least one olm.bundle.object bundle property" + failure_num=$((failure_num + 1)) + TESTPASSED=false + fi fi - popd + fi + + note="Task $(context.task.name) completed: Check result for task result." + if [ $TESTPASSED == false ]; then + ERROR_OUTPUT=$(make_result_json -r FAILURE -f $failure_num -s $((check_num - failure_num)) -t "$note") + echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + else + TEST_OUTPUT=$(make_result_json -r SUCCESS -s $check_num -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + fi + popd + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + workingDir: $(workspaces.workspace.path)/hacbs/$(context.task.name) + workspaces: + - name: workspace diff --git a/task/fbc-validation/0.2/fbc-validation.yaml b/task/fbc-validation/0.2/fbc-validation.yaml new file mode 100644 index 0000000000..3c2166bb35 --- /dev/null +++ b/task/fbc-validation/0.2/fbc-validation.yaml @@ -0,0 +1,282 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2025-01-31T00:00:00Z" + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: konflux + labels: + app.kubernetes.io/version: "0.2" + name: fbc-validation +spec: + description: Ensures file-based catalog (FBC) components are uniquely linted for + proper construction as part of build pipeline. + params: + - description: Fully qualified image name. + name: IMAGE_URL + - description: Image digest. + name: IMAGE_DIGEST + - description: Fully qualified base image name. + name: BASE_IMAGE + results: + - description: Tekton task test output. + name: TEST_OUTPUT + steps: + - computeResources: + limits: + memory: 4Gi + requests: + cpu: 10m + memory: 512Mi + env: + - name: IMAGE_URL + value: $(params.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(params.IMAGE_DIGEST) + - name: BASE_IMAGE + value: $(params.BASE_IMAGE) + image: quay.io/konflux-ci/konflux-test:v1.4.11@sha256:540f795828852c90ec8f7d1b7b5e66e88700dc3dfe45d9cad7e2b8f64217bea8 + name: extract-and-check-binaries + script: | + #!/usr/bin/env bash + set -euo pipefail + # shellcheck source=/dev/null + source /utils.sh + trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT + + declare -a ALLOWED_BASE_IMAGES=( + "registry.redhat.io/openshift4/ose-operator-registry" + "registry.redhat.io/openshift4/ose-operator-registry-rhel9" + "brew.registry.redhat.io/rh-osbs/openshift-ose-operator-registry-rhel9" + ) + + ### FBC base image check + if [ -z "${BASE_IMAGE}" ]; then + echo "Base image is unknown. The file-based catalog must have base image defined. Check inspect-image task log." + note="Task $(context.task.name) failed: The file-based catalog must have base image defined. For details, check Tekton task result TEST_OUTPUT in task inspect-image." + TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi + + IMAGE_WITHOUT_TAG=$(echo "${BASE_IMAGE}" | sed "s/:.*$//" | sed "s/@.*$//") + + allowed=false + for value in "${ALLOWED_BASE_IMAGES[@]}" + do + if [[ "${IMAGE_WITHOUT_TAG}" == "${value}" ]]; then + allowed=true + break + fi + done + + if [[ "${allowed}" == false ]]; then + # shellcheck disable=SC2128 + echo "Base image ${BASE_IMAGE} is not allowed for the file based catalog image. Allowed images: ${ALLOWED_BASE_IMAGES}" + note="Task $(context.task.name) failed: Base image ${BASE_IMAGE} is not allowed for the file based catalog image. For details, check Tekton task logs" + TEST_OUTPUT=$(make_result_json -r FAILURE -f 1 -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi + + ### Try to extract binaries with configs > check binaries functionality > check opm validate ### + if [ ! -s ../inspect-image/image_inspect.json ]; then + echo "File $(workspaces.workspace.path)/hacbs/inspect-image/image_inspect.json did not generate correctly. Check inspect-image task log." + note="Task $(context.task.name) failed: $(workspaces.workspace.path)/hacbs/inspect-image/image_inspect.json did not generate correctly. For details, check Tekton task result TEST_OUTPUT in task inspect-image." + TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi + + if [ ! -s ../inspect-image/raw_image_inspect.json ]; then + echo "File $(workspaces.workspace.path)/hacbs/inspect-image/raw_image_inspect.json did not generate correctly. Check inspect-image task log." + note="Task $(context.task.name) failed: $(workspaces.workspace.path)/hacbs/inspect-image/raw_image_inspect.json did not generate correctly. For details, check Tekton task result TEST_OUTPUT in task inspect-image." + TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi + + echo "Getting base image for source image ${IMAGE_URL}." + base_image_name="$(jq -r ".annotations.\"org.opencontainers.image.base.name\"" ../inspect-image/raw_image_inspect.json)" || status=$? + if [ "$base_image_name" == 'null' ]; then + echo "Could not get annotations from inspect-image/raw_image_inspect.json. Make sure file exists and it contains this annotation: org.opencontainers.image.base.name" + echo "Try to get base image from label..." + base_image_name="$(jq -r ".Labels.\"org.opencontainers.image.base.name\"" ../inspect-image/image_inspect.json)" || status=$? + if [ "$base_image_name" == 'null' ]; then + echo "Cannot get base image info from Labels. For details, check source image ../inspect-image/image_inspect.json." + TEST_OUTPUT="$(make_result_json -r ERROR)" + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi + fi + if [ -z "$base_image_name" ]; then + echo "Source image ${IMAGE_URL} is built from scratch, so there is no base image." + TEST_OUTPUT="$(make_result_json -r ERROR)" + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi + + status=0 + conffolder=$(jq -r '.Labels ."operators.operatorframework.io.index.configs.v1"' ../inspect-image/image_inspect.json) || status=$? + if [ $status -ne 0 ]; then + echo "Could not get labels from inspect-image/image_inspect.json. Make sure file exists and it contains this label: operators.operatorframework.io.index.configs.v1." + TEST_OUTPUT="$(make_result_json -r ERROR)" + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 0 + fi + mkdir -p /tmp/image-content confdir + pushd /tmp/image-content + image_with_digest="${IMAGE_URL}@${IMAGE_DIGEST}" + + if ! oc image extract --registry-config ~/.docker/config.json "${image_with_digest}" ; then + echo "Unable to extract or validate extracted binaries." + note="Task $(context.task.name) failed: Failed to extract image with oc extract command, so it cannot validate extracted binaries. For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + popd + exit 0 + fi + + # shellcheck disable=SC2086 + if [ -z "$(ls -A .$conffolder)" ]; then + echo "$conffolder is missing catalog file." + TEST_OUTPUT="$(make_result_json -r ERROR)" + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + popd + exit 0 + fi + # copy content of conffolder to confdir - will be used in next task - related image check + # shellcheck disable=SC2086 + cp -r .$conffolder/* "$(workspaces.workspace.path)/hacbs/$(context.task.name)/confdir" + + EXTRACT_DIR="/extracted_base_img" + mkdir "${EXTRACT_DIR}" + if ! oc image extract "${BASE_IMAGE}" --path /:"${EXTRACT_DIR}"; then + echo "Unable to extract opm binary" + note="Task $(context.task.name) failed: Failed to extract base image with oc extract command, so it cannot validate extracted binaries. For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + exit 0 + fi + + OPM_BINARIES="$(find "${EXTRACT_DIR}" -type f -name opm)" + BINARIES_COUNT=$(wc -l <<< "${OPM_BINARIES}") + if [[ $BINARIES_COUNT -ne "1" ]]; then + note="Task $(context.task.name) failed: Expected exactly one opm binary in base image. For details, check Tekton task log" + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + echo "found $BINARIES_COUNT opm binaries:" + echo "${OPM_BINARIES}" + exit 0 + fi + OPM_BINARY=$(echo "${OPM_BINARIES}" | head -n 1) + echo "OPM_BINARY: '${OPM_BINARY}'" + chmod 775 "$OPM_BINARY" + + # We have 6 total checks + check_num=6 + failure_num=0 + TESTPASSED=true + + if [[ ! $(find . -name "grpc_health_probe") ]]; then + echo "!FAILURE! - grpc_health_probe binary presence check failed." + failure_num=$((failure_num + 1)) + TESTPASSED=false + fi + + if ! ${OPM_BINARY} validate ."${conffolder}"; then + echo "!FAILURE! - opm validate check failed." + failure_num=$((failure_num + 1)) + TESTPASSED=false + fi + + OPM_RENDERED_CATALOG=/tmp/catalog.json + ${OPM_BINARY} render ."${conffolder}" > ${OPM_RENDERED_CATALOG} + if [ ! -f ${OPM_RENDERED_CATALOG} ]; then + echo "!FAILURE! - unable to render the fragment FBC." + failure_num=$((failure_num + 1)) + TESTPASSED=false + fi + + if jq -en 'reduce (inputs | select(.schema == "olm.package")) as $obj (0; .+1) < 1' ${OPM_RENDERED_CATALOG}; then + echo "!FAILURE! - There are no olm package entries defined in this FBC fragment." + failure_num=$((failure_num + 1)) + TESTPASSED=false + fi + + # examines the base_image_name tag to derive the target OCP version + # assumes this is in the form + # image-path:[v]major-digits.minor-digits[@sha...] + OCP_VER_FROM_BASE=$(echo "${base_image_name}" | sed -e "s/@.*$//" -e "s/^.*://") # strips hash first due to greedy match + # extracts major digits and filters out any leading alphabetic characters, for e.g. 'v4' --> '4' + OCP_VER_MAJOR=$(echo "${OCP_VER_FROM_BASE}" | cut -d '.' -f 1 | sed "s/^[a-zA-Z]*//") + OCP_VER_MINOR=$(echo "${OCP_VER_FROM_BASE}" | cut -d '.' -f 2) + + RUN_OCP_VERSION_VALIDATION="false" + digits_regex='^[0-9]*$' + if [[ ${OCP_VER_MAJOR} =~ $digits_regex ]] && [[ ${OCP_VER_MINOR} =~ $digits_regex ]] ; then + RUN_OCP_VERSION_VALIDATION="true" + fi + + if [ "${RUN_OCP_VERSION_VALIDATION}" == "false" ] ; then + echo "!WARNING! - unable to assess bundle metadata alignment with OCP version because we cannot extract version info from base_image_name: ${base_image_name}" + else + OCP_BUNDLE_METADATA_THRESHOLD_MAJOR=4 + OCP_BUNDLE_METADATA_THRESHOLD_MINOR=17 + OCP_BUNDLE_METADATA_FORMAT="olm.bundle.object" + + if [[ "${OCP_VER_MAJOR}" -ge "${OCP_BUNDLE_METADATA_THRESHOLD_MAJOR}" ]] && [[ "${OCP_VER_MINOR}" -ge "${OCP_BUNDLE_METADATA_THRESHOLD_MINOR}" ]]; then + OCP_BUNDLE_METADATA_FORMAT="olm.csv.metadata" + fi + + # enforce the presence of either olm.csv.metadata or olm.bundle.object based on OCP version + if [[ "${OCP_BUNDLE_METADATA_FORMAT}" = "olm.csv.metadata" ]]; then + if ! jq -en 'reduce( inputs | select(.schema == "olm.bundle" and .properties[].type == "olm.bundle.object")) as $_ (0;.+1) == 0' ${OPM_RENDERED_CATALOG}; then + echo "!FAILURE! - olm.bundle.object bundle properties are not permitted in a FBC fragment for OCP version ${OCP_VER_MAJOR}.${OCP_VER_MINOR}. Fragments must move to olm.csv.metadata bundle metadata." + failure_num=$((failure_num + 1)) + TESTPASSED=false + fi + else + if ! jq -en 'reduce( inputs | select(.schema == "olm.bundle" and .properties[].type == "olm.csv.metadata")) as $_ (0;.+1) == 0' ${OPM_RENDERED_CATALOG}; then + echo "!FAILURE! - olm.csv.metadata bundle properties are not permitted in a FBC fragment for OCP version ${OCP_VER_MAJOR}.${OCP_VER_MINOR}. Fragments must only use olm.bundle.object bundle metadata." + failure_num=$((failure_num + 1)) + TESTPASSED=false + fi + fi + + # enforce that each bundle has the OCP-version-appropriate bundle metadata. + BUNDLE_COUNT=$(jq -en 'def count(stream): reduce stream as $i (0; .+1); count(inputs|select(.schema=="olm.bundle"))' ${OPM_RENDERED_CATALOG}) + BUNDLE_BO_COUNT=$(jq -en 'def count(stream): reduce stream as $i (0; .+1); count(inputs|select(.schema == "olm.bundle" and .properties[].type == "olm.bundle.object"))' ${OPM_RENDERED_CATALOG}) + BUNDLE_CM_COUNT=$(jq -en 'def count(stream): reduce stream as $i (0; .+1); count(inputs|select(.schema == "olm.bundle" and .properties[].type == "olm.csv.metadata"))' ${OPM_RENDERED_CATALOG}) + + if [[ "${OCP_BUNDLE_METADATA_FORMAT}" = "olm.csv.metadata" ]]; then + if [[ "${BUNDLE_COUNT}" -ne "${BUNDLE_CM_COUNT}" ]]; then + echo "!FAILURE! - every olm.bundle object in the fragment must have a corresponding olm.csv.metadata bundle property" + failure_num=$((failure_num + 1)) + TESTPASSED=false + fi + else + if [[ "${BUNDLE_BO_COUNT}" -lt "${BUNDLE_COUNT}" ]]; then + echo "!FAILURE! - every olm.bundle object in the fragment must have at least one olm.bundle.object bundle property" + failure_num=$((failure_num + 1)) + TESTPASSED=false + fi + fi + fi + + note="Task $(context.task.name) completed: Check result for task result." + if [ $TESTPASSED == false ]; then + ERROR_OUTPUT=$(make_result_json -r FAILURE -f $failure_num -s $((check_num - failure_num)) -t "$note") + echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + else + TEST_OUTPUT=$(make_result_json -r SUCCESS -s $check_num -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + fi + popd + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + workingDir: $(workspaces.workspace.path)/hacbs/$(context.task.name) + workspaces: + - name: workspace diff --git a/task/inspect-image/0.1/inspect-image.yaml b/task/inspect-image/0.1/inspect-image.yaml index 46f44741f9..5e59ccc2a4 100644 --- a/task/inspect-image/0.1/inspect-image.yaml +++ b/task/inspect-image/0.1/inspect-image.yaml @@ -1,54 +1,43 @@ ---- apiVersion: tekton.dev/v1beta1 kind: Task metadata: - labels: - app.kubernetes.io/version: "0.1" annotations: build.appstudio.redhat.com/expires-on: "2025-01-31T00:00:00Z" - tekton.dev/pipelines.minVersion: "0.12.1" - tekton.dev/tags: "konflux" + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: konflux + labels: + app.kubernetes.io/version: "0.1" name: inspect-image spec: - description: >- - Inspects and analyzes manifest data of the container's source image, and its base image (if available) using Skopeo. - An image's manifest data contains information about the layers that make up the image, the platforms for which the image is intended, and other metadata about the image. + description: Inspects and analyzes manifest data of the container's source image, + and its base image (if available) using Skopeo. An image's manifest data contains + information about the layers that make up the image, the platforms for which the + image is intended, and other metadata about the image. params: - - name: IMAGE_URL - description: Fully qualified image name. - - name: IMAGE_DIGEST - description: Image digest. - type: string - - name: DOCKER_AUTH - description: unused, should be removed in next task version - default: "" - type: string + - description: Fully qualified image name. + name: IMAGE_URL + - description: Image digest. + name: IMAGE_DIGEST + type: string + - default: "" + description: unused, should be removed in next task version + name: DOCKER_AUTH + type: string results: - - description: Base image source image is built from. - name: BASE_IMAGE - - description: Base image repository URL. - name: BASE_IMAGE_REPOSITORY - - description: Tekton task test output. - name: TEST_OUTPUT - workspaces: - - name: source + - description: Base image source image is built from. + name: BASE_IMAGE + - description: Base image repository URL. + name: BASE_IMAGE_REPOSITORY + - description: Tekton task test output. + name: TEST_OUTPUT steps: - - name: inspect-image + - env: + - name: IMAGE_URL + value: $(params.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(params.IMAGE_DIGEST) image: quay.io/konflux-ci/konflux-test:v1.4.11@sha256:540f795828852c90ec8f7d1b7b5e66e88700dc3dfe45d9cad7e2b8f64217bea8 - # per https://kubernetes.io/docs/concepts/containers/images/#imagepullpolicy-defaulting - # the cluster will set imagePullPolicy to IfNotPresent - workingDir: $(workspaces.source.path)/hacbs/$(context.task.name) - securityContext: - runAsUser: 0 - capabilities: - add: - - SETFCAP - env: - - name: IMAGE_URL - value: $(params.IMAGE_URL) - - name: IMAGE_DIGEST - value: $(params.IMAGE_DIGEST) - + name: inspect-image script: | #!/usr/bin/env bash set -euo pipefail @@ -169,3 +158,11 @@ spec: note="Task $(context.task.name) completed: Check inspected JSON files under $(workspaces.source.path)/hacbs/$(context.task.name)." TEST_OUTPUT=$(make_result_json -r SUCCESS -s 1 -t "$note") echo "${TEST_OUTPUT}" | tee $(results.TEST_OUTPUT.path) + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + workingDir: $(workspaces.source.path)/hacbs/$(context.task.name) + workspaces: + - name: source diff --git a/task/inspect-image/0.2/inspect-image.yaml b/task/inspect-image/0.2/inspect-image.yaml new file mode 100644 index 0000000000..6b10eac9bd --- /dev/null +++ b/task/inspect-image/0.2/inspect-image.yaml @@ -0,0 +1,168 @@ +apiVersion: tekton.dev/v1beta1 +kind: Task +metadata: + annotations: + build.appstudio.redhat.com/expires-on: "2025-01-31T00:00:00Z" + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: konflux + labels: + app.kubernetes.io/version: "0.2" + name: inspect-image +spec: + description: Inspects and analyzes manifest data of the container's source image, + and its base image (if available) using Skopeo. An image's manifest data contains + information about the layers that make up the image, the platforms for which the + image is intended, and other metadata about the image. + params: + - description: Fully qualified image name. + name: IMAGE_URL + - description: Image digest. + name: IMAGE_DIGEST + type: string + - default: "" + description: unused, should be removed in next task version + name: DOCKER_AUTH + type: string + results: + - description: Base image source image is built from. + name: BASE_IMAGE + - description: Base image repository URL. + name: BASE_IMAGE_REPOSITORY + - description: Tekton task test output. + name: TEST_OUTPUT + steps: + - env: + - name: IMAGE_URL + value: $(params.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(params.IMAGE_DIGEST) + image: quay.io/konflux-ci/konflux-test:v1.4.11@sha256:540f795828852c90ec8f7d1b7b5e66e88700dc3dfe45d9cad7e2b8f64217bea8 + name: inspect-image + script: | + #!/usr/bin/env bash + set -euo pipefail + source /utils.sh + trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT + + IMAGE_INSPECT=image_inspect.json + BASE_IMAGE_INSPECT=base_image_inspect.json + RAW_IMAGE_INSPECT=raw_image_inspect.json + + IMAGE_URL="${IMAGE_URL}@${IMAGE_DIGEST}" + # Given a tag and a the digest in the IMAGE_URL we opt to use the digest alone + # this is because containers/image currently doesn't support image references + # that contain both. See https://github.com/containers/image/issues/1736 + if [[ "${IMAGE_URL}" == *":"*"@"* ]]; then + IMAGE_URL="${IMAGE_URL/:*@/@}" + fi + + status=-1 + max_run=5 + sleep_sec=10 + for run in $(seq 1 $max_run); do + status=0 + [ "$run" -gt 1 ] && sleep $sleep_sec # skip last sleep + echo "Inspecting manifest for source image ${IMAGE_URL} (try $run/$max_run)." + skopeo inspect --no-tags docker://"${IMAGE_URL}" > $IMAGE_INSPECT && break || status=$? + done + if [ "$status" -ne 0 ]; then + echo "Failed to inspect image ${IMAGE_URL}" + note="Task $(context.task.name) failed: Encountered errors while inspecting image. For details, check Tekton task log." + TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${TEST_OUTPUT}" | tee $(results.TEST_OUTPUT.path) + exit 0 + fi + echo "Image ${IMAGE_URL} metadata:" + cat "$IMAGE_INSPECT" + + run=1 + while [ "$run" -le "$max_run" ]; do + status=0 + [ "$run" -gt 1 ] && sleep $sleep_sec # skip last sleep + echo "Inspecting raw image manifest ${IMAGE_URL} (try $run/$max_run)." + skopeo inspect --no-tags --raw docker://"${IMAGE_URL}" > $RAW_IMAGE_INSPECT || status=$? + + if [ "$status" -eq 0 ] && [ "$(jq 'has("manifests")' ${RAW_IMAGE_INSPECT})" = "true" ]; then + echo "Found an image index, lookup for amd64 manifest" + INDEX_IMAGE_MANIFESTS=$(jq ' .manifests | map ( {(.platform.architecture|tostring|ascii_downcase): .digest} ) | add' "${RAW_IMAGE_INSPECT}" || true) + + AMD64_MANIFEST_DIGEST=$(jq -r '.amd64' <<< "${INDEX_IMAGE_MANIFESTS}" || true ) + if [ -z "$AMD64_MANIFEST_DIGEST" ]; then + # we didn't find amd64 platform, fail horribly as it's the required platform currently for all checks + echo "[ERROR] Could not find amd64 image manifest for image $IMAGE_URL" + note="Task $(context.task.name) failed: Couldn't find amd64 image manifest" + TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${TEST_OUTPUT}" | tee $(results.TEST_OUTPUT.path) + exit 0 + fi + + # Replace image URL with new digest + IMAGE_URL="${IMAGE_URL/[@:]*/@$AMD64_MANIFEST_DIGEST}" + echo "Setting AMD64 specific image: $IMAGE_URL" + run=1 # reset runs, we are looking another image; new image, new life + else + break + fi + done + + if [ "$status" -ne 0 ]; then + echo "Failed to get raw metadata of image ${IMAGE_URL}" + note="Task $(context.task.name) failed: Encountered errors while inspecting image. For details, check Tekton task log." + TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${TEST_OUTPUT}" | tee $(results.TEST_OUTPUT.path) + exit 0 + fi + + echo "Image ${IMAGE_URL} raw metadata:" + cat "$RAW_IMAGE_INSPECT" | jq # jq for readable formatting + + echo "Getting base image manifest for source image ${IMAGE_URL}." + BASE_IMAGE_NAME="$(jq -r ".annotations.\"org.opencontainers.image.base.name\"" $RAW_IMAGE_INSPECT)" + BASE_IMAGE_DIGEST="$(jq -r ".annotations.\"org.opencontainers.image.base.digest\"" $RAW_IMAGE_INSPECT)" + if [ $BASE_IMAGE_NAME == 'null' ]; then + echo "Cannot get base image info from annotations." + BASE_IMAGE_NAME="$(jq -r ".Labels.\"org.opencontainers.image.base.name\"" $IMAGE_INSPECT)" + BASE_IMAGE_DIGEST="$(jq -r ".annotations.\"org.opencontainers.image.base.digest\"" $IMAGE_INSPECT)" + if [ "$BASE_IMAGE_NAME" == 'null' ]; then + echo "Cannot get base image info from Labels. For details, check source image ${IMAGE_URL}." + exit 0 + fi + fi + if [ -z "$BASE_IMAGE_NAME" ]; then + echo "Source image ${IMAGE_URL} is built from scratch, so there is no base image." + exit 0 + fi + + BASE_IMAGE="${BASE_IMAGE_NAME%:*}@$BASE_IMAGE_DIGEST" + echo "Detected base image: $BASE_IMAGE" + echo -n "$BASE_IMAGE" > $(results.BASE_IMAGE.path) + + for run in $(seq 1 $max_run); do + status=0 + [ "$run" -gt 1 ] && sleep $sleep_sec # skip last sleep + echo "Inspecting base image ${BASE_IMAGE} (try $run/$max_run)." + skopeo inspect --no-tags "docker://$BASE_IMAGE" > $BASE_IMAGE_INSPECT && break || status=$? + done + if [ "$status" -ne 0 ]; then + echo "Failed to inspect base image ${BASE_IMAGE}" + note="Task $(context.task.name) failed: Encountered errors while inspecting image. For details, check Tekton task log." + TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${TEST_OUTPUT}" | tee $(results.TEST_OUTPUT.path) + exit 0 + fi + + BASE_IMAGE_REPOSITORY="$(jq -r '.Name | sub("[^/]+/"; "") | sub("[:@].*"; "")' "$BASE_IMAGE_INSPECT")" + echo "Detected base image repository: $BASE_IMAGE_REPOSITORY" + echo -n "$BASE_IMAGE_REPOSITORY" > $(results.BASE_IMAGE_REPOSITORY.path) + + note="Task $(context.task.name) completed: Check inspected JSON files under $(workspaces.source.path)/hacbs/$(context.task.name)." + TEST_OUTPUT=$(make_result_json -r SUCCESS -s 1 -t "$note") + echo "${TEST_OUTPUT}" | tee $(results.TEST_OUTPUT.path) + securityContext: + capabilities: + add: + - SETFCAP + runAsUser: 0 + workingDir: $(workspaces.source.path)/hacbs/$(context.task.name) + workspaces: + - name: source diff --git a/task/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml b/task/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml index e2d2c004f0..220c2196cd 100644 --- a/task/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml +++ b/task/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml @@ -1,208 +1,181 @@ ---- -# IMPORTANT: This Task definition exists in this repository as a means to facilitate integration -# with RHTAP. It is NOT meant to be used in Konflux. If you are debugging EC failures in Konflux, -# this is NOT the file you are looking for. Do NOT directly modify this file. Any change should -# first be done in the https://github.com/enterprise-contract/ec-cli repository then synced to this -# repository. Any pull request that modifies anything other than this comment in this file and is -# not a sync from the ec-cli repository will be immediately closed. See -# https://github.com/enterprise-contract/ec-cli/blob/main/tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml apiVersion: tekton.dev/v1 kind: Task metadata: - name: verify-enterprise-contract annotations: tekton.dev/displayName: Verify Enterprise Contract tekton.dev/pipelines.minVersion: "0.19" tekton.dev/tags: ec, chains, signature, conftest labels: app.kubernetes.io/version: "0.1" + name: verify-enterprise-contract spec: description: Verify the enterprise contract is met params: - - name: IMAGES - type: string - description: | - Spec section of an ApplicationSnapshot resource. Not all fields of the - resource are required. A minimal example: - { - "components": [ - { - "containerImage": "quay.io/example/repo:latest" - } - ] - } - Each "containerImage" in the "components" array is validated. - - name: POLICY_CONFIGURATION - type: string - description: | - Name of the policy configuration (EnterpriseContractPolicy - resource) to use. `namespace/name` or `name` syntax supported. If - namespace is omitted the namespace where the task runs is used. - default: "enterprise-contract-service/default" - - - name: PUBLIC_KEY - type: string - description: >- - Public key used to verify signatures. Must be a valid k8s cosign - reference, e.g. k8s://my-space/my-secret where my-secret contains - the expected cosign.pub attribute. - default: "" - - - name: REKOR_HOST - type: string - description: Rekor host for transparency log lookups - default: "" - - - name: IGNORE_REKOR - type: string - description: >- - Skip Rekor transparency log checks during validation. - default: "false" - - - name: TUF_MIRROR - type: string - description: TUF mirror URL. Provide a value when NOT using public sigstore deployment. - default: "" - - - name: SSL_CERT_DIR - type: string - description: | - Path to a directory containing SSL certs to be used when communicating - with external services. This is useful when using the integrated registry - and a local instance of Rekor on a development cluster which may use - certificates issued by a not-commonly trusted root CA. In such cases, - "/var/run/secrets/kubernetes.io/serviceaccount" is a good value. Multiple - paths can be provided by using the ":" separator. - default: "" - - - name: INFO - type: string - description: Include rule titles and descriptions in the output. Set to "false" to disable it. - default: "true" - - - name: STRICT - type: string - description: Fail the task if policy fails. Set to "false" to disable it. - default: "true" - - - name: HOMEDIR - type: string - description: Value for the HOME environment variable. - default: /tekton/home - - - name: EFFECTIVE_TIME - type: string - description: Run policy checks with the provided time. - default: "now" - - workspaces: - - name: data - description: The workspace where the snapshot spec json file resides - optional: true - + - description: | + Spec section of an ApplicationSnapshot resource. Not all fields of the + resource are required. A minimal example: + { + "components": [ + { + "containerImage": "quay.io/example/repo:latest" + } + ] + } + Each "containerImage" in the "components" array is validated. + name: IMAGES + type: string + - default: enterprise-contract-service/default + description: | + Name of the policy configuration (EnterpriseContractPolicy + resource) to use. `namespace/name` or `name` syntax supported. If + namespace is omitted the namespace where the task runs is used. + name: POLICY_CONFIGURATION + type: string + - default: "" + description: Public key used to verify signatures. Must be a valid k8s cosign + reference, e.g. k8s://my-space/my-secret where my-secret contains the expected + cosign.pub attribute. + name: PUBLIC_KEY + type: string + - default: "" + description: Rekor host for transparency log lookups + name: REKOR_HOST + type: string + - default: "false" + description: Skip Rekor transparency log checks during validation. + name: IGNORE_REKOR + type: string + - default: "" + description: TUF mirror URL. Provide a value when NOT using public sigstore deployment. + name: TUF_MIRROR + type: string + - default: "" + description: | + Path to a directory containing SSL certs to be used when communicating + with external services. This is useful when using the integrated registry + and a local instance of Rekor on a development cluster which may use + certificates issued by a not-commonly trusted root CA. In such cases, + "/var/run/secrets/kubernetes.io/serviceaccount" is a good value. Multiple + paths can be provided by using the ":" separator. + name: SSL_CERT_DIR + type: string + - default: "true" + description: Include rule titles and descriptions in the output. Set to "false" + to disable it. + name: INFO + type: string + - default: "true" + description: Fail the task if policy fails. Set to "false" to disable it. + name: STRICT + type: string + - default: /tekton/home + description: Value for the HOME environment variable. + name: HOMEDIR + type: string + - default: now + description: Run policy checks with the provided time. + name: EFFECTIVE_TIME + type: string results: - - name: TEST_OUTPUT - description: Short summary of the policy evaluation for each image - + - description: Short summary of the policy evaluation for each image + name: TEST_OUTPUT stepTemplate: env: - - name: HOME - value: "$(params.HOMEDIR)" - + - name: HOME + value: $(params.HOMEDIR) steps: - - name: version - image: registry.redhat.io/rhtas/ec-rhel9:0.2 - command: [ec] - args: - - version - - name: initialize-tuf - image: registry.redhat.io/rhtas/ec-rhel9:0.2 - script: |- - set -euo pipefail + - args: + - version + command: + - ec + image: registry.redhat.io/rhtas/ec-rhel9:0.2 + name: version + - env: + - name: TUF_MIRROR + value: $(params.TUF_MIRROR) + image: registry.redhat.io/rhtas/ec-rhel9:0.2 + name: initialize-tuf + script: |- + set -euo pipefail - if [[ -z "${TUF_MIRROR:-}" ]]; then - echo 'TUF_MIRROR not set. Skipping TUF root initialization.' - exit - fi + if [[ -z "${TUF_MIRROR:-}" ]]; then + echo 'TUF_MIRROR not set. Skipping TUF root initialization.' + exit + fi - echo 'Initializing TUF root...' - cosign initialize --mirror "${TUF_MIRROR}" --root "${TUF_MIRROR}/root.json" - echo 'Done!' - env: - - name: TUF_MIRROR - value: "$(params.TUF_MIRROR)" - - name: validate - image: registry.redhat.io/rhtas/ec-rhel9:0.2 - command: [ec] - args: - - validate - - image - - "--verbose" - - "--images" - - "$(params.IMAGES)" - - "--policy" - - "$(params.POLICY_CONFIGURATION)" - - "--public-key" - - "$(params.PUBLIC_KEY)" - - "--rekor-url" - - "$(params.REKOR_HOST)" - - "--ignore-rekor=$(params.IGNORE_REKOR)" - # NOTE: The syntax below is required to negate boolean parameters - - "--info=$(params.INFO)" - - "--strict=false" - - "--show-successes" - - "--effective-time=$(params.EFFECTIVE_TIME)" - - "--output" - - "yaml=$(params.HOMEDIR)/report.yaml" - - "--output" - - "appstudio=$(results.TEST_OUTPUT.path)" - - "--output" - - "json=$(params.HOMEDIR)/report-json.json" - env: - - name: SSL_CERT_DIR - # The Tekton Operator automatically sets the SSL_CERT_DIR env to the value below but, - # of course, without the $(param.SSL_CERT_DIR) bit. When a Task Step sets it to a - # value, the Tekton Operator does not do any processing of the value. However, Tekton - # Pipelines will fail to execute because some of these values are required for its - # execution. As a workaround, append the SSL_CERT_DIR value from params to the default - # value expected by Tekton Pipelines. NOTE: If params.SSL_CERT_DIR is empty, the value - # will contain a trailing ":" - this is ok. - value: "/tekton-custom-certs:/etc/ssl/certs:/etc/pki/tls/certs:/system/etc/security/cacerts:$(params.SSL_CERT_DIR)" - # The EC cache is used to avoid fetching the same image layers from the registry more than - # once. However, this is not thread safe. This results in inconsistencies when extracting - # files from an image, see https://github.com/enterprise-contract/ec-cli/issues/1109 - - name: EC_CACHE - value: "false" - computeResources: - requests: - cpu: 250m - memory: 2Gi - limits: - memory: 2Gi - - name: report - image: registry.redhat.io/rhtas/ec-rhel9:0.2 - command: [cat] - args: - - "$(params.HOMEDIR)/report.yaml" - - name: report-json - image: registry.redhat.io/rhtas/ec-rhel9:0.2 - command: [cat] - args: - - "$(params.HOMEDIR)/report-json.json" - - name: summary - image: registry.redhat.io/rhtas/ec-rhel9:0.2 - command: [jq] - args: - - "." - - "$(results.TEST_OUTPUT.path)" - - name: assert - image: registry.redhat.io/rhtas/ec-rhel9:0.2 - command: [jq] - args: - - "--argjson" - - "strict" - - "$(params.STRICT)" - - "-e" - - > - .result == "SUCCESS" or .result == "WARNING" or ($strict | not) - - "$(results.TEST_OUTPUT.path)" + echo 'Initializing TUF root...' + cosign initialize --mirror "${TUF_MIRROR}" --root "${TUF_MIRROR}/root.json" + echo 'Done!' + - args: + - validate + - image + - --verbose + - --images + - $(params.IMAGES) + - --policy + - $(params.POLICY_CONFIGURATION) + - --public-key + - $(params.PUBLIC_KEY) + - --rekor-url + - $(params.REKOR_HOST) + - --ignore-rekor=$(params.IGNORE_REKOR) + - --info=$(params.INFO) + - --strict=false + - --show-successes + - --effective-time=$(params.EFFECTIVE_TIME) + - --output + - yaml=$(params.HOMEDIR)/report.yaml + - --output + - appstudio=$(results.TEST_OUTPUT.path) + - --output + - json=$(params.HOMEDIR)/report-json.json + command: + - ec + computeResources: + limits: + memory: 2Gi + requests: + cpu: 250m + memory: 2Gi + env: + - name: SSL_CERT_DIR + value: /tekton-custom-certs:/etc/ssl/certs:/etc/pki/tls/certs:/system/etc/security/cacerts:$(params.SSL_CERT_DIR) + - name: EC_CACHE + value: "false" + image: registry.redhat.io/rhtas/ec-rhel9:0.2 + name: validate + - args: + - $(params.HOMEDIR)/report.yaml + command: + - cat + image: registry.redhat.io/rhtas/ec-rhel9:0.2 + name: report + - args: + - $(params.HOMEDIR)/report-json.json + command: + - cat + image: registry.redhat.io/rhtas/ec-rhel9:0.2 + name: report-json + - args: + - . + - $(results.TEST_OUTPUT.path) + command: + - jq + image: registry.redhat.io/rhtas/ec-rhel9:0.2 + name: summary + - args: + - --argjson + - strict + - $(params.STRICT) + - -e + - | + .result == "SUCCESS" or .result == "WARNING" or ($strict | not) + - $(results.TEST_OUTPUT.path) + command: + - jq + image: registry.redhat.io/rhtas/ec-rhel9:0.2 + name: assert + workspaces: + - description: The workspace where the snapshot spec json file resides + name: data + optional: true