diff --git a/ci-operator/config/quay/quay/OWNERS b/ci-operator/config/quay/quay/OWNERS index ca97a8448b03e..5d80b406bcfb0 100644 --- a/ci-operator/config/quay/quay/OWNERS +++ b/ci-operator/config/quay/quay/OWNERS @@ -17,6 +17,7 @@ approvers: - nasonawa - sridipta - sunandadadi +- nasonawa options: {} reviewers: - aroyoredhat @@ -31,3 +32,4 @@ reviewers: - nasonawa - sridipta - sunandadadi +- nasonawa \ No newline at end of file diff --git a/ci-operator/config/quay/quay/quay-quay-redhat-3.18__aws-ocp422-e2e-install.yaml b/ci-operator/config/quay/quay/quay-quay-redhat-3.18__aws-ocp422-e2e-install.yaml new file mode 100644 index 0000000000000..369f4bfddfa15 --- /dev/null +++ b/ci-operator/config/quay/quay/quay-quay-redhat-3.18__aws-ocp422-e2e-install.yaml @@ -0,0 +1,80 @@ +build_root: + image_stream_tag: + name: release + namespace: openshift + tag: rhel-9-release-golang-1.25-openshift-4.22 +images: + items: + - dockerfile_literal: | + FROM src + RUN dnf module enable -y nodejs:22 && \ + dnf install -y nodejs npm \ + alsa-lib atk at-spi2-atk cups-libs libdrm libXcomposite \ + libXdamage libXrandr mesa-libgbm pango nss nss-util nspr \ + libxkbcommon libXfixes libXcursor libXext libXi libXtst \ + libwayland-client && \ + dnf clean all + WORKDIR /go/src/github.com/quay/quay/web + RUN npm ci + ENV PLAYWRIGHT_BROWSERS_PATH=/opt/playwright + RUN npx playwright install chromium + from: src + to: quay-playwright-runner +releases: + latest: + candidate: + architecture: amd64 + product: ocp + stream: nightly + version: "4.22" +resources: + '*': + limits: + memory: 4Gi + requests: + cpu: 100m + memory: 200Mi +tests: +- as: aws-s3-3-18-nightly-4-22 + cron: '@weekly' + steps: + cluster_profile: aws-quay-qe + env: + BASE_DOMAIN: quayqe.devcluster.openshift.com + COMPUTE_NODE_TYPE: m5.4xlarge + ENABLE_BUILD_SUPPORT: "true" + MULTISTAGE_PARAM_OVERRIDE_QUAY_INDEX_IMAGE: quay.io/redhat-user-workloads/quay-eng-tenant/stable-3-18-v4-22@sha256:2a17e0af09b8d6242c8fb758da9a7a38829bdda5232a7b9f02f119c842dc2e9f + QUAY_BUILDER_IMAGE: registry.redhat.io/quay/quay-builder-rhel8:v3.18.0 + QUAY_EXTRA_CONFIG: | + FEATURE_PROGRAMMATIC_BOOTSTRAP: false + FEATURE_USER_METADATA: true + FEATURE_IMMUTABLE_TAGS: true + FEATURE_SPARSE_INDEX: true + FEATURE_MAILING: true + MAIL_SERVER: mailpit.quay-enterprise.svc.cluster.local + MAIL_PORT: 1025 + MAIL_USE_TLS: false + MAIL_USE_AUTH: false + MAIL_DEFAULT_SENDER: quay@quayqe.local + DEFAULT_UI: react + SUPER_USERS: + - admin + GLOBAL_READONLY_SUPER_USERS: + - readonly + QUAY_OPERATOR_CHANNEL: stable-3.18 + QUAY_OPERATOR_SOURCE: fbc-operator-catalog + post: + - ref: quay-operator-deprovision + test: + - ref: quay-operator-enable-catalogsource + - ref: quay-operator-provisioning-tls + - ref: quay-operator-provisioning-builder + - ref: quay-operator-deploy-mailpit + - ref: quay-operator-deploy-aws-s3 + - ref: quay-operator-test-e2e + workflow: ipi-aws +zz_generated_metadata: + branch: redhat-3.18 + org: quay + repo: quay + variant: aws-ocp422-e2e-install diff --git a/ci-operator/jobs/quay/quay/quay-quay-redhat-3.18-periodics.yaml b/ci-operator/jobs/quay/quay/quay-quay-redhat-3.18-periodics.yaml new file mode 100644 index 0000000000000..bf99a5fc533f5 --- /dev/null +++ b/ci-operator/jobs/quay/quay/quay-quay-redhat-3.18-periodics.yaml @@ -0,0 +1,84 @@ +periodics: +- agent: kubernetes + cluster: build07 + cron: '@weekly' + decorate: true + decoration_config: + skip_cloning: true + extra_refs: + - base_ref: redhat-3.18 + org: quay + repo: quay + labels: + ci-operator.openshift.io/cloud: aws + ci-operator.openshift.io/cloud-cluster-profile: aws-quay-qe + ci-operator.openshift.io/variant: aws-ocp422-e2e-install + ci.openshift.io/generator: prowgen + job-release: "4.22" + pj-rehearse.openshift.io/can-be-rehearsed: "true" + name: periodic-ci-quay-quay-redhat-3.18-aws-ocp422-e2e-install-aws-s3-3-18-nightly-4-22 + spec: + containers: + - args: + - --gcs-upload-secret=/secrets/gcs/service-account.json + - --image-import-pull-secret=/etc/pull-secret/.dockerconfigjson + - --lease-server-credentials-file=/etc/boskos/credentials + - --report-credentials-file=/etc/report/credentials + - --secret-dir=/secrets/ci-pull-credentials + - --target=aws-s3-3-18-nightly-4-22 + - --variant=aws-ocp422-e2e-install + command: + - ci-operator + env: + - name: HTTP_SERVER_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + image: quay-proxy.ci.openshift.org/openshift/ci:ci_ci-operator_latest + imagePullPolicy: Always + name: "" + ports: + - containerPort: 8080 + name: http + resources: + requests: + cpu: 10m + volumeMounts: + - mountPath: /etc/boskos + name: boskos + readOnly: true + - mountPath: /secrets/ci-pull-credentials + name: ci-pull-credentials + readOnly: true + - mountPath: /secrets/gcs + name: gcs-credentials + readOnly: true + - mountPath: /secrets/manifest-tool + name: manifest-tool-local-pusher + readOnly: true + - mountPath: /etc/pull-secret + name: pull-secret + readOnly: true + - mountPath: /etc/report + name: result-aggregator + readOnly: true + serviceAccountName: ci-operator + volumes: + - name: boskos + secret: + items: + - key: credentials + path: credentials + secretName: boskos-credentials + - name: ci-pull-credentials + secret: + secretName: ci-pull-credentials + - name: manifest-tool-local-pusher + secret: + secretName: manifest-tool-local-pusher + - name: pull-secret + secret: + secretName: registry-pull-credentials + - name: result-aggregator + secret: + secretName: result-aggregator diff --git a/ci-operator/jobs/quay/quay/quay-quay-redhat-3.18-presubmits.yaml b/ci-operator/jobs/quay/quay/quay-quay-redhat-3.18-presubmits.yaml new file mode 100644 index 0000000000000..505f3b598c059 --- /dev/null +++ b/ci-operator/jobs/quay/quay/quay-quay-redhat-3.18-presubmits.yaml @@ -0,0 +1,60 @@ +presubmits: + quay/quay: + - agent: kubernetes + always_run: true + branches: + - ^redhat-3\.18$ + - ^redhat-3\.18- + cluster: build06 + context: ci/prow/aws-ocp422-e2e-install-images + decorate: true + decoration_config: + skip_cloning: true + labels: + ci-operator.openshift.io/variant: aws-ocp422-e2e-install + ci.openshift.io/generator: prowgen + job-release: "4.22" + pj-rehearse.openshift.io/can-be-rehearsed: "true" + name: pull-ci-quay-quay-redhat-3.18-aws-ocp422-e2e-install-images + rerun_command: /test aws-ocp422-e2e-install-images + spec: + containers: + - args: + - --gcs-upload-secret=/secrets/gcs/service-account.json + - --image-import-pull-secret=/etc/pull-secret/.dockerconfigjson + - --report-credentials-file=/etc/report/credentials + - --target=[images] + - --variant=aws-ocp422-e2e-install + command: + - ci-operator + image: quay-proxy.ci.openshift.org/openshift/ci:ci_ci-operator_latest + imagePullPolicy: Always + name: "" + resources: + requests: + cpu: 10m + volumeMounts: + - mountPath: /secrets/gcs + name: gcs-credentials + readOnly: true + - mountPath: /secrets/manifest-tool + name: manifest-tool-local-pusher + readOnly: true + - mountPath: /etc/pull-secret + name: pull-secret + readOnly: true + - mountPath: /etc/report + name: result-aggregator + readOnly: true + serviceAccountName: ci-operator + volumes: + - name: manifest-tool-local-pusher + secret: + secretName: manifest-tool-local-pusher + - name: pull-secret + secret: + secretName: registry-pull-credentials + - name: result-aggregator + secret: + secretName: result-aggregator + trigger: (?m)^/test( | .* )aws-ocp422-e2e-install-images,?($|\s.*) diff --git a/ci-operator/step-registry/quay-operator/OWNERS b/ci-operator/step-registry/quay-operator/OWNERS new file mode 100644 index 0000000000000..bd932d737f687 --- /dev/null +++ b/ci-operator/step-registry/quay-operator/OWNERS @@ -0,0 +1,4 @@ +approvers: +- quay-approvers +reviewers: +- quay-approvers diff --git a/ci-operator/step-registry/quay-operator/deploy-aws-s3/OWNERS b/ci-operator/step-registry/quay-operator/deploy-aws-s3/OWNERS new file mode 120000 index 0000000000000..ec405d65a79df --- /dev/null +++ b/ci-operator/step-registry/quay-operator/deploy-aws-s3/OWNERS @@ -0,0 +1 @@ +../OWNERS \ No newline at end of file diff --git a/ci-operator/step-registry/quay-operator/deploy-aws-s3/quay-operator-deploy-aws-s3-commands.sh b/ci-operator/step-registry/quay-operator/deploy-aws-s3/quay-operator-deploy-aws-s3-commands.sh new file mode 100644 index 0000000000000..e9b3a68f8a503 --- /dev/null +++ b/ci-operator/step-registry/quay-operator/deploy-aws-s3/quay-operator-deploy-aws-s3-commands.sh @@ -0,0 +1,417 @@ +#!/bin/bash + +set -o nounset +set -o errexit +set -o pipefail + +if [ "${MAP_TESTS}" = "true" ]; then + eval "$( + typeset -a _fURL=() + type -t wget 1>/dev/null && _fURL=(wget -qO-) || _fURL=(curl -fsSL) + "${_fURL[@]}" \ +https://raw.githubusercontent.com/RedHatQE/OpenShift-LP-QE--Tools/refs/heads/main/libs/bash/ci-operator/interop/common/ExitTrap--PostProcessPrep.sh + )"; trap ' + LP_IO__ET_PPP__NEW_TS_NAME="${DR__RP__CR_COMP_NAME}--%s" \ + ExitTrap--PostProcessPrep junit--quay-tests__deploy-quay-aws-s3__quay-tests-deploy-quay-aws-s3.xml + ' EXIT +fi + +QUAY_NS="quay-enterprise" + +function archive_pod_info() { + local ns="${QUAY_NS}" + echo "Archiving pod status and logs from namespace ${ns}..." + oc get pods -n "${ns}" -o wide > "${ARTIFACT_DIR}/pods_status.txt" 2>&1 || true + oc get pods -n "${ns}" -o yaml > "${ARTIFACT_DIR}/pods_full.yaml" 2>&1 || true + mkdir -p "${ARTIFACT_DIR}/pod_logs" + while read -r pod; do + [[ -z "${pod}" ]] && continue + containers=$(oc get pod "${pod}" -n "${ns}" -o jsonpath='{.spec.initContainers[*].name} {.spec.containers[*].name}' 2>/dev/null || true) + for container in ${containers}; do + oc logs "${pod}" -n "${ns}" -c "${container}" > "${ARTIFACT_DIR}/pod_logs/${pod}_${container}.log" 2>&1 || true + oc logs "${pod}" -n "${ns}" -c "${container}" --previous > "${ARTIFACT_DIR}/pod_logs/${pod}_${container}_previous.log" 2>&1 || true + done + done < <(oc get pods -n "${ns}" -o jsonpath='{.items[*].metadata.name}' 2>/dev/null | tr ' ' '\n') +} + +function print_quayregistry_conditions() { + local ns="${QUAY_NS}" + if command -v jq >/dev/null 2>&1; then + oc -n "${ns}" get quayregistry quay -o json 2>/dev/null \ + | jq -r '.status.conditions[]? | "\(.type)=\(.status) reason=\(.reason // "") msg=\(.message // "")"' >&2 || true + else + oc -n "${ns}" get quayregistry quay -o yaml 2>/dev/null >&2 || true + fi +} + +function print_failing_pod_logs() { + local ns="${QUAY_NS}" + local name ready status restarts age + while read -r name ready status restarts age; do + [[ -z "${name}" ]] && continue + case "${status}" in + CrashLoopBackOff|Error|ErrImagePull|ImagePullBackOff|CreateContainerConfigError|CreateContainerError|OOMKilled|Init:CrashLoopBackOff|Init:Error|Failed) ;; + *) continue ;; + esac + echo "===== ${name} status=${status} restarts=${restarts} =====" >&2 + local containers + containers=$(oc get pod "${name}" -n "${ns}" -o jsonpath='{.spec.initContainers[*].name} {.spec.containers[*].name}' 2>/dev/null || true) + for container in ${containers}; do + echo "----- ${name}/${container} (tail 80) -----" >&2 + oc logs "${name}" -n "${ns}" -c "${container}" --tail=80 2>&1 || true + echo "----- ${name}/${container} previous (tail 40) -----" >&2 + oc logs "${name}" -n "${ns}" -c "${container}" --previous --tail=40 2>&1 || true + done + done < <(oc get pods -n "${ns}" --no-headers 2>/dev/null || true) +} + +#Get the credentials and Email of new Quay User +QUAY_USERNAME=$(cat /var/run/quay-qe-quay-secret/username) +QUAY_PASSWORD=$(cat /var/run/quay-qe-quay-secret/password) +QUAY_EMAIL=$(cat /var/run/quay-qe-quay-secret/email) + +# Create AWS S3 storage bucket. Names are globally unique; $RANDOM (0-32767) +# collides with leftover quay-tests buckets that use the same quayprowci prefix. +function new_s3_bucket_name() { + local suffix + suffix="${NAMESPACE:-ns}-${UNIQUE_HASH:-$(date +%s)}-${RANDOM}-$(date +%s)" + suffix="$(printf '%s' "${suffix}" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9-')" + printf 'quayprowci-%s\n' "${suffix}" +} + +#Create AWS S3 Storage Bucket +QUAY_OPERATOR_CHANNEL="$QUAY_OPERATOR_CHANNEL" +QUAY_OPERATOR_SOURCE="$QUAY_OPERATOR_SOURCE" + +QUAY_AWS_ACCESS_KEY=$(cat /var/run/quay-qe-aws-secret/access_key) +QUAY_AWS_SECRET_KEY=$(cat /var/run/quay-qe-aws-secret/secret_key) + +export AWS_ACCESS_KEY_ID="${QUAY_AWS_ACCESS_KEY}" +export AWS_SECRET_ACCESS_KEY="${QUAY_AWS_SECRET_KEY}" + +mkdir -p QUAY_AWS && cd QUAY_AWS +cat >>variables.tf <>create_aws_bucket.tf <&2 + terraform destroy -auto-approve || true +done + +#Share Terraform Var and Terraform Directory for deprovision on success and failure +echo "${QUAY_AWS_S3_BUCKET}" > ${SHARED_DIR}/QUAY_AWS_S3_BUCKET +tar -cvzf terraform.tgz --exclude=".terraform" * +cp terraform.tgz ${SHARED_DIR} + +if [[ "${tf_apply_rc}" -ne 0 ]]; then + echo "terraform apply failed with exit code ${tf_apply_rc}" >&2 + exit "${tf_apply_rc}" +fi + +#Deploy Quay Operator to OCP namespace 'quay-enterprise' +cat <&2 + echo "=== CSV Status ===" >&2 + oc -n quay-enterprise get csv -o wide 2>&1 || true + echo "=== Subscription Status ===" >&2 + oc -n quay-enterprise get subscription quay-operator -o jsonpath='{.status}' 2>&1 || true + echo "" >&2 + echo "=== CatalogSource Status ===" >&2 + oc get catalogsource -n openshift-marketplace -o wide 2>&1 || true + archive_pod_info + exit 1 +fi +echo "Quay Operator is deployed successfully" + +echo "Waiting for QuayRegistry CRD to be available..." +for _ in {1..30}; do + if oc get crd quayregistries.quay.redhat.com &>/dev/null; then + echo "QuayRegistry CRD is available" + break + fi + sleep 5 +done +if ! oc get crd quayregistries.quay.redhat.com &>/dev/null; then + echo "Timed out waiting for QuayRegistry CRD" >&2 + echo "=== Operator Pod Logs ===" >&2 + oc logs -n quay-enterprise -l name=quay-operator --tail=100 2>&1 || true + echo "=== Events ===" >&2 + oc get events -n quay-enterprise --sort-by='.lastTimestamp' 2>&1 | tail -30 || true + archive_pod_info + exit 1 +fi + +#Deploy Quay, here disable monitoring component +cat >>config.yaml <extra_config.yaml + curl -sL "https://github.com/mikefarah/yq/releases/latest/download/yq_linux_$(uname -m | sed 's/aarch64/arm64/;s/x86_64/amd64/')" \ + -o /tmp/yq && chmod +x /tmp/yq + /tmp/yq eval-all -i 'select(fileIndex == 0) *+ select(fileIndex == 1)' config.yaml extra_config.yaml + # Strip field-group keys for components this CR keeps managed. The operator + # injects those values; leaving them in configBundleSecret blocks rollout. + /tmp/yq -i ' + del( + .FEATURE_SECURITY_SCANNER, + .FEATURE_SECURITY_NOTIFICATIONS, + .SECURITY_SCANNER_ENDPOINT, + .SECURITY_SCANNER_INDEXING_INTERVAL, + .SECURITY_SCANNER_V4_ENDPOINT, + .SECURITY_SCANNER_V4_NAMESPACE_WHITELIST, + .SECURITY_SCANNER_V4_PSK, + .FEATURE_REPO_MIRROR, + .REPO_MIRROR_INTERVAL, + .REPO_MIRROR_SERVER_HOSTNAME, + .REPO_MIRROR_TLS_VERIFY, + .BUILDLOGS_REDIS, + .USER_EVENTS_REDIS, + .DB_URI, + .DB_CONNECTION_ARGS, + .SERVER_HOSTNAME, + .PREFERRED_URL_SCHEME, + .EXTERNAL_TLS_TERMINATION + ) + ' config.yaml +fi + +# Build support requires unmanaged TLS plus a virtual builder. When enabled, the +# quay-operator-provisioning-{tls,builder} steps have already written the +# cert/key, build-cluster CA, and builder config to SHARED_DIR. Fold the builder +# config into the bundle (after the extra-config merge, so it is not stripped) and +# hand the operator the unmanaged cert material. Do not echo config_builder.yaml: +# it carries the Quay password and builder SA token. (This script runs without +# set -x, so the append below is not traced.) +TLS_MANAGED="true" +if [[ "${ENABLE_BUILD_SUPPORT:-false}" == "true" ]]; then + echo "Build support enabled: configuring unmanaged TLS + virtual builder" >&2 + for f in config_builder.yaml ssl.cert ssl.key build_cluster.crt; do + if [[ ! -s "${SHARED_DIR}/${f}" ]]; then + echo "ERROR: ENABLE_BUILD_SUPPORT=true but ${SHARED_DIR}/${f} is missing." >&2 + echo " Ensure quay-operator-provisioning-tls and -builder ran first." >&2 + exit 1 + fi + done + TLS_MANAGED="false" + cat "${SHARED_DIR}/config_builder.yaml" >> config.yaml + + oc create secret generic -n quay-enterprise config-bundle-secret \ + --from-file config.yaml=./config.yaml \ + --from-file ssl.cert="${SHARED_DIR}/ssl.cert" \ + --from-file ssl.key="${SHARED_DIR}/ssl.key" \ + --from-file extra_ca_cert_build_cluster.crt="${SHARED_DIR}/build_cluster.crt" +else + oc create secret generic -n quay-enterprise --from-file config.yaml=./config.yaml config-bundle-secret +fi + +echo "Creating Quay registry..." >&2 +cat <&2 +for i in $(seq 1 90); do + status="$(oc -n "${QUAY_NS}" get quayregistry quay -o jsonpath='{.status.conditions[?(@.type=="Available")].status}' 2>/dev/null || true)" + if [[ "$status" == "True" ]]; then + echo "Quay is ready (after $((i * 10))s)" >&2 + oc -n "${QUAY_NS}" get quayregistries -o yaml >"$ARTIFACT_DIR/quayregistries.yaml" + oc get quayregistry quay -n "${QUAY_NS}" -o jsonpath='{.status.registryEndpoint}' > "$SHARED_DIR"/quayroute || true + quay_route=$(oc get quayregistry quay -n "${QUAY_NS}" -o jsonpath='{.status.registryEndpoint}') || true + curl -k -X POST $quay_route/api/v1/user/initialize --header 'Content-Type: application/json' \ + --data '{ "username": "'$QUAY_USERNAME'", "password": "'$QUAY_PASSWORD'", "email": "'$QUAY_EMAIL'", "access_token": true }' | jq '.access_token' | tr -d '"' | tr -d '\n' > "$SHARED_DIR"/quay_oauth2_token || true + archive_pod_info + exit 0 + fi + if (( i % 6 == 0 )); then + echo "[$((i * 10))s] Quay not ready yet. Component status:" >&2 + print_quayregistry_conditions + fi + sleep 10 +done + +echo "Timed out waiting for Quay to become ready" >&2 +echo "Final QuayRegistry conditions:" >&2 +print_quayregistry_conditions +echo "Pods in ${QUAY_NS} namespace:" >&2 +oc -n "${QUAY_NS}" get pods -o wide >&2 || true +print_failing_pod_logs +echo "Events in ${QUAY_NS} namespace:" >&2 +oc -n "${QUAY_NS}" get events --sort-by='.lastTimestamp' >&2 || true + +oc -n "${QUAY_NS}" get quayregistries -o yaml >"$ARTIFACT_DIR/quayregistries.yaml" || true +oc -n "${QUAY_NS}" get pods -o yaml >"$ARTIFACT_DIR/quay-pods.yaml" || true +oc -n "${QUAY_NS}" get events --sort-by='.lastTimestamp' -o yaml >"$ARTIFACT_DIR/quay-events.yaml" || true +oc -n "${QUAY_NS}" get deployments -o yaml >"$ARTIFACT_DIR/quay-deployments.yaml" || true +archive_pod_info +exit 1 + diff --git a/ci-operator/step-registry/quay-operator/deploy-aws-s3/quay-operator-deploy-aws-s3-ref.metadata.json b/ci-operator/step-registry/quay-operator/deploy-aws-s3/quay-operator-deploy-aws-s3-ref.metadata.json new file mode 100644 index 0000000000000..221b795be695e --- /dev/null +++ b/ci-operator/step-registry/quay-operator/deploy-aws-s3/quay-operator-deploy-aws-s3-ref.metadata.json @@ -0,0 +1,11 @@ +{ + "path": "quay-operator/deploy-aws-s3/quay-operator-deploy-aws-s3-ref.yaml", + "owners": { + "approvers": [ + "quay-approvers" + ], + "reviewers": [ + "quay-approvers" + ] + } +} \ No newline at end of file diff --git a/ci-operator/step-registry/quay-operator/deploy-aws-s3/quay-operator-deploy-aws-s3-ref.yaml b/ci-operator/step-registry/quay-operator/deploy-aws-s3/quay-operator-deploy-aws-s3-ref.yaml new file mode 100644 index 0000000000000..8ecf7aee7c2bf --- /dev/null +++ b/ci-operator/step-registry/quay-operator/deploy-aws-s3/quay-operator-deploy-aws-s3-ref.yaml @@ -0,0 +1,52 @@ +ref: + as: quay-operator-deploy-aws-s3 + cli: latest + from_image: + name: quay-test-console + namespace: ci + tag: latest + commands: quay-operator-deploy-aws-s3-commands.sh + timeout: 8h0m0s + grace_period: 10m + resources: + requests: + cpu: 10m + memory: 100Mi + credentials: + - namespace: test-credentials + name: quay-qe-aws-secret + mount_path: /var/run/quay-qe-aws-secret + - namespace: test-credentials + name: quay-qe-quay-secret + mount_path: /var/run/quay-qe-quay-secret + documentation: |- + Deploy Quay Operator and Quay + env: + - name: DR__RP__CR_COMP_NAME + default: "lp-ocp-compat--Quay" + documentation: ReportPortal Component Readiness component name for junit remapping + - name: MAP_TESTS + default: "false" + documentation: Specify whether to update the test suite name for reporting tools + - name: QUAY_OPERATOR_CHANNEL + documentation: The quay operator channel + default: "stable-3.16" + - name: QUAY_OPERATOR_SOURCE + documentation: The quay operator source + default: "redhat-operators" + - name: QUAY_EXTRA_CONFIG + documentation: |- + Additional Quay configuration YAML merged on top of the default config. + Values provided here override defaults. Supports flat keys and nested + structures. Example: "FEATURE_QUOTA_MANAGEMENT: false" + default: "" + - name: ENABLE_BUILD_SUPPORT + documentation: |- + When "true", deploy Quay with virtual-builder build support: switch the + QuayRegistry tls component to unmanaged, fold ${SHARED_DIR}/config_builder.yaml + (FEATURE_BUILD_SUPPORT + BUILDMAN) into the config bundle, and include the + unmanaged TLS cert/key and build-cluster CA in config-bundle-secret. Requires + the quay-operator-provisioning-tls and -builder steps to have run first + (they produce ssl.cert, ssl.key, build_cluster.crt, config_builder.yaml in + SHARED_DIR). When "false", Quay is deployed with managed TLS and no builder. + default: "false" \ No newline at end of file diff --git a/ci-operator/step-registry/quay-operator/deploy-mailpit/OWNERS b/ci-operator/step-registry/quay-operator/deploy-mailpit/OWNERS new file mode 120000 index 0000000000000..ec405d65a79df --- /dev/null +++ b/ci-operator/step-registry/quay-operator/deploy-mailpit/OWNERS @@ -0,0 +1 @@ +../OWNERS \ No newline at end of file diff --git a/ci-operator/step-registry/quay-operator/deploy-mailpit/quay-operator-deploy-mailpit-commands.sh b/ci-operator/step-registry/quay-operator/deploy-mailpit/quay-operator-deploy-mailpit-commands.sh new file mode 100755 index 0000000000000..4fc2fb60c48b4 --- /dev/null +++ b/ci-operator/step-registry/quay-operator/deploy-mailpit/quay-operator-deploy-mailpit-commands.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +set -o nounset +set -o errexit +set -o pipefail + +# Deploy Mailpit as an in-cluster SMTP sink + HTTP API for Quay mailing tests. +# Quay (in-cluster) sends SMTP to the mailpit Service on 1025; the Playwright pod +# (external) reads delivered mail over the HTTP API exposed via an edge Route. +# No credentials are handled here, so tracing (set -x) is safe to omit anyway. + +ARTIFACT_DIR=${ARTIFACT_DIR:=/tmp/artifacts} +mkdir -p "${ARTIFACT_DIR}" + +QUAY_NS="${QUAYNAMESPACE}" + +# Ensure the namespace exists (deploy-aws-s3 also creates it; be order-independent). +oc get namespace "${QUAY_NS}" >/dev/null 2>&1 || oc create namespace "${QUAY_NS}" + +echo "Deploying Mailpit (${MAILPIT_IMAGE}) into ${QUAY_NS}..." +cat </dev/null || true) + [[ -n "${MAILPIT_HOST}" ]] && break + sleep 5 +done +if [[ -z "${MAILPIT_HOST}" ]]; then + echo "ERROR: Mailpit route host not available" >&2 + oc get route mailpit -n "${QUAY_NS}" -o yaml >&2 || true + exit 1 +fi + +# utils/mailpit.ts reads MAILPIT_API_URL and calls `${MAILPIT_API_URL}/messages`; +# Mailpit serves that under /api/v1, so the base URL includes the /api/v1 segment. +# The test step exports this file's contents as MAILPIT_API_URL. +MAILPIT_API_URL="https://${MAILPIT_HOST}/api/v1" +echo "${MAILPIT_API_URL}" > "${SHARED_DIR}/mailpit_api" +cp "${SHARED_DIR}/mailpit_api" "${ARTIFACT_DIR}/mailpit_api" || true +echo "Mailpit API: ${MAILPIT_API_URL}" diff --git a/ci-operator/step-registry/quay-operator/deploy-mailpit/quay-operator-deploy-mailpit-ref.metadata.json b/ci-operator/step-registry/quay-operator/deploy-mailpit/quay-operator-deploy-mailpit-ref.metadata.json new file mode 100644 index 0000000000000..fb3c9a6ce3be3 --- /dev/null +++ b/ci-operator/step-registry/quay-operator/deploy-mailpit/quay-operator-deploy-mailpit-ref.metadata.json @@ -0,0 +1,11 @@ +{ + "path": "quay-operator/deploy-mailpit/quay-operator-deploy-mailpit-ref.yaml", + "owners": { + "approvers": [ + "quay-approvers" + ], + "reviewers": [ + "quay-approvers" + ] + } +} \ No newline at end of file diff --git a/ci-operator/step-registry/quay-operator/deploy-mailpit/quay-operator-deploy-mailpit-ref.yaml b/ci-operator/step-registry/quay-operator/deploy-mailpit/quay-operator-deploy-mailpit-ref.yaml new file mode 100644 index 0000000000000..e99a99dac0972 --- /dev/null +++ b/ci-operator/step-registry/quay-operator/deploy-mailpit/quay-operator-deploy-mailpit-ref.yaml @@ -0,0 +1,28 @@ +ref: + as: quay-operator-deploy-mailpit + cli: latest + from_image: + name: quay-test-omr + namespace: ci + tag: latest + commands: quay-operator-deploy-mailpit-commands.sh + resources: + requests: + cpu: 10m + memory: 100Mi + grace_period: 10m0s + documentation: |- + Deploy a Mailpit SMTP sink into the Quay namespace so Quay's FEATURE_MAILING + has a mail server to send to, and the Playwright e2e suite has an HTTP API to + read delivered mail from (utils/mailpit.ts). Creates a Deployment, a Service + exposing SMTP (1025) and HTTP (8025), and an edge-terminated Route to the HTTP + API. Writes the API base URL to ${SHARED_DIR}/mailpit_api for the test step. + env: + - name: QUAYNAMESPACE + default: "quay-enterprise" + documentation: Namespace where Quay (and Mailpit) are deployed. + - name: MAILPIT_IMAGE + default: "docker.io/axllent/mailpit:latest" + documentation: |- + Mailpit container image. Override to a pinned/mirrored tag for reproducible + runs and to avoid registry rate limits. diff --git a/ci-operator/step-registry/quay-operator/deprovision/OWNERS b/ci-operator/step-registry/quay-operator/deprovision/OWNERS new file mode 120000 index 0000000000000..ec405d65a79df --- /dev/null +++ b/ci-operator/step-registry/quay-operator/deprovision/OWNERS @@ -0,0 +1 @@ +../OWNERS \ No newline at end of file diff --git a/ci-operator/step-registry/quay-operator/deprovision/quay-operator-deprovision-commands.sh b/ci-operator/step-registry/quay-operator/deprovision/quay-operator-deprovision-commands.sh new file mode 100644 index 0000000000000..c441623bdc0d0 --- /dev/null +++ b/ci-operator/step-registry/quay-operator/deprovision/quay-operator-deprovision-commands.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +set -o nounset +set -o errexit +set -o pipefail + +sleep 8s +if [[ "$QUAY_STORAGE_PROVIDER" == 'gcp' ]]; then + #Copy GCP auth.json from mounted secret to current directory + mkdir -p QUAY_GCP && cd QUAY_GCP + cp /var/run/quay-qe-gcp-secret/auth.json . + cp ${SHARED_DIR}/terraform.tgz . + tar -xzvf terraform.tgz && ls + + QUAY_GCP_STORAGE_ID=$(cat ${SHARED_DIR}/QUAY_GCP_STORAGE_ID) + echo "Start to destroy quay gcp bucket $QUAY_GCP_STORAGE_ID ..." + + export TF_VAR_gcp_storage_bucket="${QUAY_GCP_STORAGE_ID}" + terraform init + terraform destroy -auto-approve || true +fi + +if [[ "$QUAY_STORAGE_PROVIDER" == 'azure' ]]; then + mkdir -p QUAY_AZURE && cd QUAY_AZURE + cp ${SHARED_DIR}/terraform.tgz . + tar -xzvf terraform.tgz && ls + + QUAY_AZURE_STORAGE_ID=$(cat ${SHARED_DIR}/QUAY_AZURE_STORAGE_ID) + echo "Start to destroy quay azure bucket $QUAY_AZURE_STORAGE_ID ..." + + export TF_VAR_resource_group="${QUAY_AZURE_STORAGE_ID}" + export TF_VAR_storage_account="${QUAY_AZURE_STORAGE_ID}" + export TF_VAR_storage_container="${QUAY_AZURE_STORAGE_ID}" + terraform init + terraform destroy -auto-approve || true +fi + + +if [[ "$QUAY_STORAGE_PROVIDER" == 'aws' ]]; then + mkdir -p QUAY_AWS && cd QUAY_AWS + cp ${SHARED_DIR}/terraform.tgz . + tar -xzvf terraform.tgz && ls + + QUAY_AWS_S3_BUCKET=$(cat ${SHARED_DIR}/QUAY_AWS_S3_BUCKET) + echo "Start to destroy quay aws bucket $QUAY_AWS_S3_BUCKET ..." + + export TF_VAR_aws_bucket="${QUAY_AWS_S3_BUCKET}" + terraform init + terraform destroy -auto-approve || true +fi + +if [[ "$QUAY_STORAGE_PROVIDER" == 'awssts' ]]; then + mkdir -p QUAY_AWSSTS && cd QUAY_AWSSTS + cp "${SHARED_DIR}/terraform.tgz" . + tar -xzvf terraform.tgz && ls + + QUAY_AWS_S3_BUCKET=$(cat "${SHARED_DIR}/QUAY_AWS_STS_S3_BUCKET") + randomnum=$(cat "${SHARED_DIR}/QUAY_AWS_STS_RANDOM") + QUAY_AWS_STS_ROLE_NAME="quay_prow_role${randomnum}" + QUAY_AWS_STS_USER="quay_prow_automation${randomnum}" + export TF_VAR_aws_bucket="${QUAY_AWS_S3_BUCKET}" + export TF_VAR_aws_sts_role_name="${QUAY_AWS_STS_ROLE_NAME}" + export TF_VAR_aws_sts_user_name="${QUAY_AWS_STS_USER}" + + echo "Start to destroy quay aws sts ..." + terraform init + terraform destroy -auto-approve || true +fi + +if [[ "$QUAY_STORAGE_PROVIDER" == 'S3CloudFront' ]]; then + mkdir -p QUAY_S3CloundFront && cd QUAY_S3CloundFront + cp "${SHARED_DIR}/terraform.s3cf.tgz" . + tar -xzvf terraform.s3cf.tgz && ls + + QUAY_AWS_S3_CF_BUCKET=$(cat "${SHARED_DIR}/QUAY_AWS_S3_CF_BUCKET") + randomnum=$(cat "${SHARED_DIR}/QUAY_AWS_CF_RANDOM") + export TF_VAR_aws_bucket="${QUAY_AWS_S3_CF_BUCKET}" + export TF_VAR_quay_s3_origin_id="quay_origin_id${randomnum}" + + echo "Start to destroy quay aws s3 cloudfront ..." + terraform init + terraform destroy -auto-approve || true +fi diff --git a/ci-operator/step-registry/quay-operator/deprovision/quay-operator-deprovision-ref.metadata.json b/ci-operator/step-registry/quay-operator/deprovision/quay-operator-deprovision-ref.metadata.json new file mode 100644 index 0000000000000..8d19cfb83cceb --- /dev/null +++ b/ci-operator/step-registry/quay-operator/deprovision/quay-operator-deprovision-ref.metadata.json @@ -0,0 +1,11 @@ +{ + "path": "quay-operator/deprovision/quay-operator-deprovision-ref.yaml", + "owners": { + "approvers": [ + "quay-approvers" + ], + "reviewers": [ + "quay-approvers" + ] + } +} \ No newline at end of file diff --git a/ci-operator/step-registry/quay-operator/deprovision/quay-operator-deprovision-ref.yaml b/ci-operator/step-registry/quay-operator/deprovision/quay-operator-deprovision-ref.yaml new file mode 100644 index 0000000000000..5d9fe416faaea --- /dev/null +++ b/ci-operator/step-registry/quay-operator/deprovision/quay-operator-deprovision-ref.yaml @@ -0,0 +1,26 @@ +ref: + as: quay-operator-deprovision + cli: latest + from_image: + name: quay-test-omr + namespace: ci + tag: latest + commands: quay-operator-deprovision-commands.sh + resources: + requests: + cpu: 10m + memory: 100Mi + credentials: + - namespace: test-credentials + name: quay-qe-omr-secret + mount_path: /var/run/quay-qe-omr-secret + - namespace: test-credentials + name: quay-qe-gcp-secret + mount_path: /var/run/quay-qe-gcp-secret + timeout: 2h0m0s + env: + - name: QUAY_STORAGE_PROVIDER + documentation: The Quay Storage Provider "aws, gcp, azure" + default: "aws" + documentation: |- + Deprovisioning Quay AWS GCP AZURE STORAGE BUCKETS \ No newline at end of file diff --git a/ci-operator/step-registry/quay-operator/enable-catalogsource/OWNERS b/ci-operator/step-registry/quay-operator/enable-catalogsource/OWNERS new file mode 120000 index 0000000000000..ec405d65a79df --- /dev/null +++ b/ci-operator/step-registry/quay-operator/enable-catalogsource/OWNERS @@ -0,0 +1 @@ +../OWNERS \ No newline at end of file diff --git a/ci-operator/step-registry/quay-operator/enable-catalogsource/quay-operator-enable-catalogsource-commands.sh b/ci-operator/step-registry/quay-operator/enable-catalogsource/quay-operator-enable-catalogsource-commands.sh new file mode 100644 index 0000000000000..def327b0a4a43 --- /dev/null +++ b/ci-operator/step-registry/quay-operator/enable-catalogsource/quay-operator-enable-catalogsource-commands.sh @@ -0,0 +1,249 @@ +#!/bin/bash + +set -o nounset +set -o errexit +set -o pipefail + +KONFLUX_REGISTRY="image-rbac-proxy.apps.stone-prd-rh01.pg1f.p1.openshiftapps.com" + +# Merge the konflux prod auth into the current ocp global pull secret +function update_pull_secret () { + + temp_dir=$(mktemp -d) + + # Generate pull auth from konflux-quay-pull-auth credentials + KONFLUX_PULL_USER=$(cat /var/run/konflux-quay-pull-auth/username) + KONFLUX_PULL_PASS=$(cat /var/run/konflux-quay-pull-auth/password) + KONFLUX_PULL_AUTH=$(echo -n "${KONFLUX_PULL_USER}:${KONFLUX_PULL_PASS}" | base64 -w0) + echo '{"auths":{"'"${KONFLUX_REGISTRY}"'":{"auth":"'"${KONFLUX_PULL_AUTH}"'"}}}' > "${temp_dir}"/konflux-quay-pull.json + + oc get secret/pull-secret -n openshift-config \ + --template='{{index .data ".dockerconfigjson" | base64decode}}' > "${temp_dir}"/global_pull_secret.json + + jq -s 'map(.auths) | add | {auths: .}' \ + "${temp_dir}"/global_pull_secret.json \ + "${temp_dir}"/konflux-quay-pull.json \ + > "${temp_dir}"/merged_pull_secret.json + + oc set data secret/pull-secret -n openshift-config --from-file=.dockerconfigjson="${temp_dir}"/merged_pull_secret.json + + #Remove temp_dir + rm -rf "${temp_dir}" +} + +function wait_mcp_ready () { + local COUNTER=0 + local STATUS="" + while [ $COUNTER -lt 1800 ] #30 min at most + do + COUNTER=$(("$COUNTER" + 30)) + echo "waiting ${COUNTER}s" + sleep 30 + STATUS="$(oc get mcp worker -o=jsonpath='{.status.conditions[?(@.type=="Updated")].status}' || true)" + if [[ $STATUS = "True" ]]; then + echo "MCP worker is ready" + return 0 + fi + done + echo "!!! MCP worker is not ready" + return 1 +} +#create image content source policy +#https://docs.redhat.com/en/documentation/openshift_container_platform/4.12/html/images/image-configuration +#ImageContentSourcePolicy is deprecated, will replace with ImageDigestMirrorSet with OCP 4.12 EOL(January 17, 2027) +function create_icsp () { + cat <>"$temp_dir"/config_builder.yaml <&2 + +ocp_base_domain_name=$(oc get dns/cluster -o jsonpath="{.spec.baseDomain}") + +#In Prow, base domain is longer, like: ci-op-w3ki37mj-cc978.qe.devcluster.openshift.com +#it's easy to meet below maxsize error if len(quay_cn_name)>64 +#encoding routines:ASN1_mbstring_ncopy:string too long:crypto/asn1/a_mbstr.c:107:maxsize=64 +quay_cn_wildcard_name="apps."$ocp_base_domain_name +quay_cn_name="quay.${quay_cn_wildcard_name}" +quay_builder_route="${QUAYREGISTRY}-quay-builder-${QUAYNAMESPACE}.${quay_cn_wildcard_name}" +quay_name="${QUAYREGISTRY}-quay-${QUAYNAMESPACE}.${quay_cn_wildcard_name}" + +temp_dir=$(mktemp -d) + +cat >>"$temp_dir"/openssl.cnf <>"$temp_dir"/ssl.cert + + if [ -e "$temp_dir"/ssl.cert ]; then + echo "Create the TLS/SSL cert file successfully" + else + echo "!!! Fail to create the TLS/SSL cert file " + return 1 + fi + +} + +#Create Artifact Directory +ARTIFACT_DIR=${ARTIFACT_DIR:=/tmp/artifacts} +mkdir -p "$ARTIFACT_DIR" + +function copyCerts { + #Copy ssl files to SHARED_DIR + echo "Copy tls certs to $SHARED_DIR folder" + mv ca.crt "$SHARED_DIR"/build_cluster.crt + cp "$temp_dir"/ssl.cert "$temp_dir"/ssl.key "$SHARED_DIR" + + #Archive the tls cert files + cp "$temp_dir"/ssl.cert "$temp_dir"/ssl.key "$ARTIFACT_DIR" + + #Clean up temp dir + rm -rf "$temp_dir" || true +} + +#Get openshift CA Cert, include into secret bundle +oc extract cm/kube-root-ca.crt -n openshift-apiserver --confirm +create_cert || true +echo "tls cert successfully created" + +#Finally Copy certs to SHARED_DIR and archive them +trap copyCerts EXIT diff --git a/ci-operator/step-registry/quay-operator/provisioning-tls/quay-operator-provisioning-tls-ref.metadata.json b/ci-operator/step-registry/quay-operator/provisioning-tls/quay-operator-provisioning-tls-ref.metadata.json new file mode 100644 index 0000000000000..d1ce6c2b08f27 --- /dev/null +++ b/ci-operator/step-registry/quay-operator/provisioning-tls/quay-operator-provisioning-tls-ref.metadata.json @@ -0,0 +1,11 @@ +{ + "path": "quay-operator/provisioning-tls/quay-operator-provisioning-tls-ref.yaml", + "owners": { + "approvers": [ + "quay-approvers" + ], + "reviewers": [ + "quay-approvers" + ] + } +} \ No newline at end of file diff --git a/ci-operator/step-registry/quay-operator/provisioning-tls/quay-operator-provisioning-tls-ref.yaml b/ci-operator/step-registry/quay-operator/provisioning-tls/quay-operator-provisioning-tls-ref.yaml new file mode 100644 index 0000000000000..40cc3ede05e34 --- /dev/null +++ b/ci-operator/step-registry/quay-operator/provisioning-tls/quay-operator-provisioning-tls-ref.yaml @@ -0,0 +1,25 @@ +ref: + as: quay-operator-provisioning-tls + cli: latest + from_image: + name: quay-test-omr + namespace: ci + tag: latest + commands: quay-operator-provisioning-tls-commands.sh + resources: + requests: + cpu: 10m + memory: 100Mi + grace_period: 15m0s + documentation: |- + Create a self-signed TLS cert/key pair for an unmanaged-TLS Quay deployment. + The certificate SANs cover both the Quay route and the Quay builder route, and + the cluster CA is captured too. Writes ssl.cert, ssl.key and build_cluster.crt + to ${SHARED_DIR} for the deploy step (required for build support / unmanaged TLS). + env: + - name: QUAYREGISTRY + documentation: The quay registry name + default: "quay" + - name: QUAYNAMESPACE + documentation: The Quay installed namespace + default: "quay-enterprise" diff --git a/ci-operator/step-registry/quay-operator/test-e2e/OWNERS b/ci-operator/step-registry/quay-operator/test-e2e/OWNERS new file mode 120000 index 0000000000000..ec405d65a79df --- /dev/null +++ b/ci-operator/step-registry/quay-operator/test-e2e/OWNERS @@ -0,0 +1 @@ +../OWNERS \ No newline at end of file diff --git a/ci-operator/step-registry/quay-operator/test-e2e/quay-operator-test-e2e-commands.sh b/ci-operator/step-registry/quay-operator/test-e2e/quay-operator-test-e2e-commands.sh new file mode 100644 index 0000000000000..ace4ef0f73bea --- /dev/null +++ b/ci-operator/step-registry/quay-operator/test-e2e/quay-operator-test-e2e-commands.sh @@ -0,0 +1,191 @@ +#!/bin/bash + +set -euo pipefail +set -x + +ARTIFACT_DIR=${ARTIFACT_DIR:=/tmp/artifacts} +mkdir -p "${ARTIFACT_DIR}" + +# Read the Quay route written by the deploy step +QUAY_ROUTE=$(cat "${SHARED_DIR}/quayroute") +if [[ -z "${QUAY_ROUTE}" ]]; then + echo "ERROR: quayroute not found in SHARED_DIR" >&2 + exit 1 +fi +echo "Quay route: ${QUAY_ROUTE}" + +# Read credentials +# Disable tracing due to password handling +[[ $- == *x* ]] && WAS_TRACING=true || WAS_TRACING=false +set +x +QUAY_USERNAME=$(cat /var/run/quay-qe-quay-secret/username) +QUAY_PASSWORD=$(cat /var/run/quay-qe-quay-secret/password) +$WAS_TRACING && set -x + +# Configure Playwright environment +# PLAYWRIGHT_BASE_URL: browser navigation URL (Quay UI) +# REACT_QUAY_APP_API_URL: backend API URL (same as UI on OCP) +export PLAYWRIGHT_BASE_URL="${QUAY_ROUTE}" +export REACT_QUAY_APP_API_URL="${QUAY_ROUTE}" +export PLAYWRIGHT_JUNIT_OUTPUT_NAME="${ARTIFACT_DIR}/junit_playwright.xml" +export PLAYWRIGHT_BROWSERS_PATH=/opt/playwright +export QUAY_USERNAME +export QUAY_PASSWORD +export CI=true + +# Mailpit HTTP API base URL for email-dependent specs. utils/mailpit.ts reads +# process.env.MAILPIT_API_URL (NOT MAILPIT_API), so the var name must match or the +# suite falls back to http://localhost:8025 and reports "Mailpit NOT available". +# Written by the quay-operator-deploy-mailpit step. Left unset when mailing is off. +if [[ -s "${SHARED_DIR}/mailpit_api" ]]; then + MAILPIT_API_URL=$(cat "${SHARED_DIR}/mailpit_api") + export MAILPIT_API_URL + echo "MAILPIT_API_URL=${MAILPIT_API_URL}" +else + echo "No mailpit_api in SHARED_DIR; email-dependent specs may skip or fail" +fi + +PLAYWRIGHT_WORKDIR="/go/src/github.com/quay/quay/web" +PLAYWRIGHT_GIT_REPO="${PLAYWRIGHT_GIT_REPO:-https://github.com/quay/quay.git}" + +# Gangway / rehearsal override wins over PLAYWRIGHT_GIT_BRANCH. +if [[ -n "${MULTISTAGE_PARAM_OVERRIDE_PLAYWRIGHT_GIT_BRANCH:-}" ]]; then + PLAYWRIGHT_GIT_BRANCH="${MULTISTAGE_PARAM_OVERRIDE_PLAYWRIGHT_GIT_BRANCH}" +fi + +clone_playwright_sources() { + local repo="$1" + local branch="$2" + local dest="$3" + + rm -rf "${dest}" + mkdir -p "${dest}" + export GIT_TERMINAL_PROMPT=0 + + if command -v git >/dev/null 2>&1; then + git clone --depth 1 --branch "${branch}" "${repo}" "${dest}" + return + fi + + echo "git is not installed; downloading archive for ${branch}..." + local archive + archive="$(mktemp /tmp/quay-src.XXXXXX.tar.gz)" + local base="${repo%.git}" + if curl -fsSL "${base}/archive/refs/heads/${branch}.tar.gz" -o "${archive}"; then + : + elif curl -fsSL "${base}/archive/refs/tags/${branch}.tar.gz" -o "${archive}"; then + : + else + echo "ERROR: failed to download ${repo} at ${branch}" >&2 + rm -f "${archive}" + exit 1 + fi + tar -xzf "${archive}" --strip-components=1 -C "${dest}" + rm -f "${archive}" +} + +# Default: use tests and browsers baked into quay-playwright-runner (same git +# ref as the image build). Clone only when PLAYWRIGHT_GIT_BRANCH is set. +if [[ -n "${PLAYWRIGHT_GIT_BRANCH:-}" ]]; then + CLONE_DIR="/tmp/quay-playwright-src" + echo "Cloning Playwright tests from ${PLAYWRIGHT_GIT_REPO} (branch ${PLAYWRIGHT_GIT_BRANCH})" + clone_playwright_sources "${PLAYWRIGHT_GIT_REPO}" "${PLAYWRIGHT_GIT_BRANCH}" "${CLONE_DIR}" + PLAYWRIGHT_WORKDIR="${CLONE_DIR}/web" + if [[ ! -d "${PLAYWRIGHT_WORKDIR}" ]]; then + echo "ERROR: cloned sources have no web/ directory at ${PLAYWRIGHT_WORKDIR}" >&2 + exit 1 + fi + + echo "Installing npm dependencies for Playwright branch ${PLAYWRIGHT_GIT_BRANCH}..." + pushd "${PLAYWRIGHT_WORKDIR}" + npm ci + + # Image browsers live in /opt/playwright as root. Test pods cannot write there. + IMAGE_BROWSERS=/opt/playwright + if [[ -d "${IMAGE_BROWSERS}" && -w "${IMAGE_BROWSERS}" ]]; then + export PLAYWRIGHT_BROWSERS_PATH="${IMAGE_BROWSERS}" + else + export PLAYWRIGHT_BROWSERS_PATH=/tmp/playwright-browsers + mkdir -p "${PLAYWRIGHT_BROWSERS_PATH}" + if [[ -d "${IMAGE_BROWSERS}" ]]; then + echo "Seeding writable browser cache from ${IMAGE_BROWSERS}..." + cp -a "${IMAGE_BROWSERS}/." "${PLAYWRIGHT_BROWSERS_PATH}/" || true + fi + fi + echo "PLAYWRIGHT_BROWSERS_PATH=${PLAYWRIGHT_BROWSERS_PATH}" + npx playwright install chromium + popd +else + echo "Using Playwright tests from image at ${PLAYWRIGHT_WORKDIR} (PLAYWRIGHT_GIT_BRANCH unset)" + if [[ ! -d "${PLAYWRIGHT_WORKDIR}" ]]; then + echo "ERROR: image is missing ${PLAYWRIGHT_WORKDIR}" >&2 + exit 1 + fi +fi + +function copyArtifacts { + echo "Copying test artifacts..." + local src="${PLAYWRIGHT_WORKDIR:-.}" + cp -r "${src}"/test-results/* "${ARTIFACT_DIR}/" 2>/dev/null || true + # Rename JUnit reports with junit_ prefix for Prow + for file in "${ARTIFACT_DIR}"/*.xml; do + if [[ -f "${file}" ]] && [[ ! "$(basename "${file}")" =~ ^junit_ ]]; then + mv "${file}" "${ARTIFACT_DIR}/junit_$(basename "${file}")" + fi + done + cp -r "${src}"/playwright-report/* "${ARTIFACT_DIR}/" 2>/dev/null || true +} +trap copyArtifacts EXIT + +# Pre-create test users so Playwright's global-setup finds them already existing. +# Without this, POST /api/v1/user/ auto-signs in the new user via common_login(), +# which calls generate_csrf_token(force=True) — replacing the session CSRF token. +# The Playwright ApiClient caches the old token and reuses it for signIn(), causing +# a CSRF mismatch (403). When users already exist, createUser() gets "already exists" +# which skips common_login(), keeping the cached CSRF token valid. +echo "Pre-creating Playwright test users..." +set +x +for USER_JSON in \ + '{"username":"admin","password":"password","email":"admin@example.com"}' \ + '{"username":"testuser","password":"password","email":"testuser@example.com"}' \ + '{"username":"readonly","password":"password","email":"readonly@example.com"}'; do + + UNAME=$(echo "${USER_JSON}" | python3 -c "import sys,json; print(json.load(sys.stdin)['username'])") + + CSRF=$(curl -sk -c /tmp/csrf_cookies -H 'X-Requested-With: XMLHttpRequest' \ + "${QUAY_ROUTE}/csrf_token" \ + | python3 -c "import sys,json; print(json.load(sys.stdin).get('csrf_token',''))" 2>/dev/null) || true + + if [[ -n "${CSRF}" ]]; then + HTTP_CODE=$(curl -sk -b /tmp/csrf_cookies -o /dev/null -w '%{http_code}' \ + -X POST "${QUAY_ROUTE}/api/v1/user/" \ + -H 'Content-Type: application/json' \ + -H "X-CSRF-Token: ${CSRF}" \ + -d "${USER_JSON}") || true + echo " ${UNAME}: ${HTTP_CODE}" + else + echo " ${UNAME}: skipped (no CSRF token)" + fi + rm -f /tmp/csrf_cookies +done +$WAS_TRACING && set -x +echo "Test user pre-creation complete" + +# IPI cluster ingress certs are not in Node's trust store. global-setup.ts uses +# Node fetch() for GET ${API_URL}/config (not Playwright request, which has +# ignoreHTTPSErrors). Without this, config fetch throws and smoke tests never run. +export NODE_TLS_REJECT_UNAUTHORIZED=0 + +# Tests excluded from the run. Beyond unsupported auth backends (OIDC/LDAP), this +# quarantines webhook tests (out of scope) plus tests tracking known product bugs +# and UI/version gaps. See E2E_FAILURE_REPORT.md for the rationale per entry. +PLAYWRIGHT_GREP_INVERT="${PLAYWRIGHT_GREP_INVERT:-@auth:OIDC|@auth:LDAP}" +echo "Excluding tests matching: ${PLAYWRIGHT_GREP_INVERT}" + +echo "Running Playwright smoke tests from ${PLAYWRIGHT_WORKDIR} (branch ${PLAYWRIGHT_GIT_BRANCH:-image})..." +pushd "${PLAYWRIGHT_WORKDIR}" +npx playwright test \ + --grep-invert "${PLAYWRIGHT_GREP_INVERT}" \ + --reporter=junit,html \ + 2>&1 | tee "${ARTIFACT_DIR}/playwright-output.log" +popd diff --git a/ci-operator/step-registry/quay-operator/test-e2e/quay-operator-test-e2e-ref.metadata.json b/ci-operator/step-registry/quay-operator/test-e2e/quay-operator-test-e2e-ref.metadata.json new file mode 100644 index 0000000000000..d60f9b37549ed --- /dev/null +++ b/ci-operator/step-registry/quay-operator/test-e2e/quay-operator-test-e2e-ref.metadata.json @@ -0,0 +1,11 @@ +{ + "path": "quay-operator/test-e2e/quay-operator-test-e2e-ref.yaml", + "owners": { + "approvers": [ + "quay-approvers" + ], + "reviewers": [ + "quay-approvers" + ] + } +} \ No newline at end of file diff --git a/ci-operator/step-registry/quay-operator/test-e2e/quay-operator-test-e2e-ref.yaml b/ci-operator/step-registry/quay-operator/test-e2e/quay-operator-test-e2e-ref.yaml new file mode 100644 index 0000000000000..d77fff0d60906 --- /dev/null +++ b/ci-operator/step-registry/quay-operator/test-e2e/quay-operator-test-e2e-ref.yaml @@ -0,0 +1,46 @@ +ref: + as: quay-operator-test-e2e + cli: latest + from: quay-playwright-runner + commands: quay-operator-test-e2e-commands.sh + resources: + requests: + cpu: "2" + memory: 4Gi + timeout: 2h0m0s + grace_period: 15m0s + credentials: + - namespace: test-credentials + name: quay-qe-quay-secret + mount_path: /var/run/quay-qe-quay-secret + env: + - name: PLAYWRIGHT_GIT_REPO + default: "https://github.com/quay/quay.git" + documentation: |- + Git repository cloned when PLAYWRIGHT_GIT_BRANCH is set. Unused when + tests run from the quay-playwright-runner image sources. + - name: PLAYWRIGHT_GIT_BRANCH + default: "" + documentation: |- + Optional git branch or tag to clone for Playwright tests. When empty, + tests run from the quay-playwright-runner image (same git ref as the + ci-operator src / image build). Set this only to override the image. + - name: MULTISTAGE_PARAM_OVERRIDE_PLAYWRIGHT_GIT_BRANCH + default: "" + documentation: |- + Optional Gangway or rehearsal override for PLAYWRIGHT_GIT_BRANCH. + When set, tests are cloned from that ref instead of using the image. + - name: QUAY_OPERATOR_CHANNEL + default: "stable-3.16" + documentation: Installed Quay operator channel (informational in this step). + - name: PLAYWRIGHT_GREP_INVERT + default: "@auth:OIDC|@auth:LDAP|@webhook|Webhook Delivery Verification|tokens? (is|are) hidden|team and members links stay correct|session cookie honors configured SESSION_TIMEOUT|Help menu|creates notifications for each event type" + documentation: |- + Regex passed to `playwright test --grep-invert` to skip tests. Defaults + exclude unsupported auth backends (OIDC/LDAP), all webhook tests (out of + scope), and tests tracking known product bugs / UI-version gaps. See + E2E_FAILURE_REPORT.md for the rationale behind each quarantined entry. + documentation: |- + Execute Quay Playwright e2e smoke tests against a deployed Quay instance. + By default tests and browsers come from the quay-playwright-runner image. + Set PLAYWRIGHT_GIT_BRANCH to clone a different quay.git ref at runtime. diff --git a/core-services/prow/02_config/openshift/controller-runtime-common/_prowconfig.yaml b/core-services/prow/02_config/openshift/controller-runtime-common/_prowconfig.yaml index 1b214e3663532..ba86388ab373d 100644 --- a/core-services/prow/02_config/openshift/controller-runtime-common/_prowconfig.yaml +++ b/core-services/prow/02_config/openshift/controller-runtime-common/_prowconfig.yaml @@ -5,6 +5,7 @@ tide: labels: - approved - lgtm + - verified missingLabels: - backports/unvalidated-commits - do-not-merge/hold diff --git a/core-services/prow/02_config/openshift/hypershift-oadp-plugin/_prowconfig.yaml b/core-services/prow/02_config/openshift/hypershift-oadp-plugin/_prowconfig.yaml index bb6554820ec78..4c9a95c447602 100644 --- a/core-services/prow/02_config/openshift/hypershift-oadp-plugin/_prowconfig.yaml +++ b/core-services/prow/02_config/openshift/hypershift-oadp-plugin/_prowconfig.yaml @@ -26,6 +26,7 @@ tide: labels: - approved - lgtm + - verified missingLabels: - backports/unvalidated-commits - do-not-merge/hold diff --git a/core-services/prow/02_config/operator-framework/operator-lifecycle-manager/_prowconfig.yaml b/core-services/prow/02_config/operator-framework/operator-lifecycle-manager/_prowconfig.yaml index 6235c99f78196..0078477e44b70 100644 --- a/core-services/prow/02_config/operator-framework/operator-lifecycle-manager/_prowconfig.yaml +++ b/core-services/prow/02_config/operator-framework/operator-lifecycle-manager/_prowconfig.yaml @@ -8,6 +8,7 @@ tide: labels: - approved - lgtm + - verified missingLabels: - backports/unvalidated-commits - do-not-merge/hold diff --git a/core-services/prow/02_config/operator-framework/operator-registry/_prowconfig.yaml b/core-services/prow/02_config/operator-framework/operator-registry/_prowconfig.yaml index b9270f79ff168..c1d191ca2f173 100644 --- a/core-services/prow/02_config/operator-framework/operator-registry/_prowconfig.yaml +++ b/core-services/prow/02_config/operator-framework/operator-registry/_prowconfig.yaml @@ -37,6 +37,21 @@ tide: - jira/invalid-bug repos: - operator-framework/operator-registry + - includedBranches: + - master + labels: + - approved + - lgtm + - verified + missingLabels: + - backports/unvalidated-commits + - do-not-merge/hold + - do-not-merge/invalid-owners-file + - do-not-merge/work-in-progress + - jira/invalid-bug + - needs-rebase + repos: + - operator-framework/operator-registry - excludedBranches: - master - release-4.0 diff --git a/core-services/sanitize-prow-jobs/_config.yaml b/core-services/sanitize-prow-jobs/_config.yaml index c769dfbd8bddc..8b81ceccf3927 100644 --- a/core-services/sanitize-prow-jobs/_config.yaml +++ b/core-services/sanitize-prow-jobs/_config.yaml @@ -4704,6 +4704,7 @@ buildFarm: - openshift-priv-sriov-cni-release-5.0-presubmits.yaml - openshift-priv-sriov-cni-release-5.2-presubmits.yaml - quay-quay-master-presubmits.yaml + - quay-quay-redhat-3.18-presubmits.yaml - openshift-cluster-svcat-controller-manager-operator-release-4.2-presubmits.yaml - openshift-cluster-svcat-controller-manager-operator-release-4.3-presubmits.yaml - openshift-cluster-svcat-controller-manager-operator-release-4.1-presubmits.yaml @@ -11542,6 +11543,7 @@ buildFarm: - openshift-priv-stolostron-obo-prometheus-operator-release-2.16-presubmits.yaml - openshift-priv-stolostron-obo-prometheus-operator-release-2.17-presubmits.yaml - quay-quay-master-periodics.yaml + - quay-quay-redhat-3.18-periodics.yaml - openshift-priv-stolostron-obo-prometheus-operator-release-5.0-presubmits.yaml - openshift-genie-web-client-main-presubmits.yaml - openshift-priv-operator-framework-olm-main-postsubmits.yaml