diff --git a/ci-operator/config/openshift/installer/openshift-installer-main.yaml b/ci-operator/config/openshift/installer/openshift-installer-main.yaml index b65d8d54ec200..578965d7eada1 100644 --- a/ci-operator/config/openshift/installer/openshift-installer-main.yaml +++ b/ci-operator/config/openshift/installer/openshift-installer-main.yaml @@ -530,6 +530,21 @@ tests: FEATURE_SET: TechPreviewNoUpgrade OS_IMAGE_STREAM: rhel-10 workflow: openshift-e2e-aws +- always_run: false + as: e2e-gcd-techpreview + cluster: build07 + optional: true + steps: + cluster_profile: gcd + env: + AWS_PROFILE: hub + BASE_DOMAIN: ci.gcd.devcluster.openshift.com + COMPUTE_NODE_REPLICAS: "2" + DEFAULT_MACHINE_OSIMAGE: rhcos10 + FEATURE_SET: TechPreviewNoUpgrade + GOOGLE_CLOUD_UNIVERSE_DOMAIN: apis-berlin-build0.goog + PUBLISH: Internal + workflow: openshift-e2e-gcd - as: e2e-gcp-ovn run_if_changed: (gcp|google) steps: diff --git a/ci-operator/jobs/openshift/installer/openshift-installer-main-presubmits.yaml b/ci-operator/jobs/openshift/installer/openshift-installer-main-presubmits.yaml index 6d356881046d9..68d15d27f84df 100644 --- a/ci-operator/jobs/openshift/installer/openshift-installer-main-presubmits.yaml +++ b/ci-operator/jobs/openshift/installer/openshift-installer-main-presubmits.yaml @@ -5803,6 +5803,96 @@ presubmits: secret: secretName: result-aggregator trigger: (?m)^/test( | .* )e2e-external-aws-ccm,?($|\s.*) + - agent: kubernetes + always_run: false + branches: + - ^main$ + - ^main- + cluster: build07 + context: ci/prow/e2e-gcd-techpreview + decorate: true + decoration_config: + sparse_checkout_files: + - .ci-operator.yaml + - images/baremetal/Dockerfile.ci + - images/installer/Dockerfile.ci + - images/installer/Dockerfile.upi.ci + - images/libvirt/Dockerfile.ci + - images/openstack/Dockerfile.ci + labels: + ci-operator.openshift.io/cloud: gcp + ci-operator.openshift.io/cloud-cluster-profile: gcd + ci-operator.openshift.io/cluster: build07 + ci.openshift.io/generator: prowgen + pj-rehearse.openshift.io/can-be-rehearsed: "true" + name: pull-ci-openshift-installer-main-e2e-gcd-techpreview + optional: true + rerun_command: /test e2e-gcd-techpreview + 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=e2e-gcd-techpreview + 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 + trigger: (?m)^/test( | .* )e2e-gcd-techpreview,?($|\s.*) - agent: kubernetes always_run: false branches: diff --git a/ci-operator/step-registry/gather/gcp-console/gather-gcp-console-commands.sh b/ci-operator/step-registry/gather/gcp-console/gather-gcp-console-commands.sh index b2b0166415d23..037185db2c0cb 100755 --- a/ci-operator/step-registry/gather/gcp-console/gather-gcp-console-commands.sh +++ b/ci-operator/step-registry/gather/gcp-console/gather-gcp-console-commands.sh @@ -7,7 +7,11 @@ set -o pipefail trap 'CHILDREN=$(jobs -p); if test -n "${CHILDREN}"; then kill ${CHILDREN} && wait; fi' TERM export GOOGLE_CLOUD_KEYFILE_JSON="${CLUSTER_PROFILE_DIR}/gce.json" -gcloud auth activate-service-account --key-file="${GOOGLE_CLOUD_KEYFILE_JSON}" +if [[ "$(jq -r .type ${GOOGLE_CLOUD_KEYFILE_JSON} 2>/dev/null)" == "external_account" ]]; then + gcloud auth login --cred-file="${GOOGLE_CLOUD_KEYFILE_JSON}" --quiet +else + gcloud auth activate-service-account --key-file="${GOOGLE_CLOUD_KEYFILE_JSON}" +fi if test ! -f "${SHARED_DIR}/metadata.json" then diff --git a/ci-operator/step-registry/gcp/deprovision/bastionhost/gcp-deprovision-bastionhost-commands.sh b/ci-operator/step-registry/gcp/deprovision/bastionhost/gcp-deprovision-bastionhost-commands.sh index 5fb75dab2f4a8..cc4e33a3dbffd 100755 --- a/ci-operator/step-registry/gcp/deprovision/bastionhost/gcp-deprovision-bastionhost-commands.sh +++ b/ci-operator/step-registry/gcp/deprovision/bastionhost/gcp-deprovision-bastionhost-commands.sh @@ -25,11 +25,17 @@ if [[ "${OSD_QE_PROJECT_AS_SERVICE_PROJECT}" == "yes" ]]; then else GOOGLE_PROJECT_ID="$(< ${CLUSTER_PROFILE_DIR}/openshift_gcp_project)" export GCP_SHARED_CREDENTIALS_FILE="${CLUSTER_PROFILE_DIR}/gce.json" - sa_email=$(jq -r .client_email ${GCP_SHARED_CREDENTIALS_FILE}) - if ! gcloud auth list | grep -E "\*\s+${sa_email}" - then - gcloud auth activate-service-account --key-file="${GCP_SHARED_CREDENTIALS_FILE}" + CRED_TYPE=$(jq -r .type "${GCP_SHARED_CREDENTIALS_FILE}" 2>/dev/null) + if [[ "${CRED_TYPE}" == "external_account" ]]; then + gcloud auth login --cred-file="${GCP_SHARED_CREDENTIALS_FILE}" --quiet gcloud config set project "${GOOGLE_PROJECT_ID}" + else + sa_email=$(jq -r .client_email ${GCP_SHARED_CREDENTIALS_FILE}) + if ! gcloud auth list | grep -E "\*\s+${sa_email}" + then + gcloud auth activate-service-account --key-file="${GCP_SHARED_CREDENTIALS_FILE}" + gcloud config set project "${GOOGLE_PROJECT_ID}" + fi fi fi diff --git a/ci-operator/step-registry/gcp/deprovision/vpc/gcp-deprovision-vpc-commands.sh b/ci-operator/step-registry/gcp/deprovision/vpc/gcp-deprovision-vpc-commands.sh index c35724101d4f3..c36252bb15f19 100755 --- a/ci-operator/step-registry/gcp/deprovision/vpc/gcp-deprovision-vpc-commands.sh +++ b/ci-operator/step-registry/gcp/deprovision/vpc/gcp-deprovision-vpc-commands.sh @@ -14,11 +14,17 @@ fi GOOGLE_PROJECT_ID="$(< ${CLUSTER_PROFILE_DIR}/openshift_gcp_project)" export GCP_SHARED_CREDENTIALS_FILE="${CLUSTER_PROFILE_DIR}/gce.json" -sa_email=$(jq -r .client_email ${GCP_SHARED_CREDENTIALS_FILE}) -if ! gcloud auth list | grep -E "\*\s+${sa_email}" -then - gcloud auth activate-service-account --key-file="${GCP_SHARED_CREDENTIALS_FILE}" +CRED_TYPE=$(jq -r .type "${GCP_SHARED_CREDENTIALS_FILE}" 2>/dev/null) +if [[ "${CRED_TYPE}" == "external_account" ]]; then + gcloud auth login --cred-file="${GCP_SHARED_CREDENTIALS_FILE}" --quiet gcloud config set project "${GOOGLE_PROJECT_ID}" +else + sa_email=$(jq -r .client_email ${GCP_SHARED_CREDENTIALS_FILE}) + if ! gcloud auth list | grep -E "\*\s+${sa_email}" + then + gcloud auth activate-service-account --key-file="${GCP_SHARED_CREDENTIALS_FILE}" + gcloud config set project "${GOOGLE_PROJECT_ID}" + fi fi ## Destroy the VPC diff --git a/ci-operator/step-registry/gcp/provision/bastionhost/gcp-provision-bastionhost-commands.sh b/ci-operator/step-registry/gcp/provision/bastionhost/gcp-provision-bastionhost-commands.sh index 6ca6d724d3cf9..653fbed5006d4 100755 --- a/ci-operator/step-registry/gcp/provision/bastionhost/gcp-provision-bastionhost-commands.sh +++ b/ci-operator/step-registry/gcp/provision/bastionhost/gcp-provision-bastionhost-commands.sh @@ -65,8 +65,8 @@ workdir=`mktemp -d` # Generally we do not update boot image for bastion host very often, we just use it as a jump # host, mirror registry, and proxy server, these services do not have frequent update. # So hard-code them here. -IMAGE_NAME="fedora-coreos-41-20241122-3-0-gcp-x86-64" -IMAGE_PROJECT="fedora-coreos-cloud" +IMAGE_NAME="${BASTION_IMAGE_NAME:-fedora-coreos-41-20241122-3-0-gcp-x86-64}" +IMAGE_PROJECT="${BASTION_IMAGE_PROJECT:-fedora-coreos-cloud}" echo "Using ${IMAGE_NAME} image from ${IMAGE_PROJECT} project" ##################################### @@ -88,11 +88,17 @@ if [[ "${OSD_QE_PROJECT_AS_SERVICE_PROJECT}" == "yes" ]]; then else GOOGLE_PROJECT_ID="$(< ${CLUSTER_PROFILE_DIR}/openshift_gcp_project)" export GCP_SHARED_CREDENTIALS_FILE="${CLUSTER_PROFILE_DIR}/gce.json" - sa_email=$(jq -r .client_email ${GCP_SHARED_CREDENTIALS_FILE}) - if ! gcloud auth list | grep -E "\*\s+${sa_email}" - then - gcloud auth activate-service-account --key-file="${GCP_SHARED_CREDENTIALS_FILE}" + CRED_TYPE=$(jq -r .type "${GCP_SHARED_CREDENTIALS_FILE}" 2>/dev/null) + if [[ "${CRED_TYPE}" == "external_account" ]]; then + gcloud auth login --cred-file="${GCP_SHARED_CREDENTIALS_FILE}" --quiet gcloud config set project "${GOOGLE_PROJECT_ID}" + else + sa_email=$(jq -r .client_email ${GCP_SHARED_CREDENTIALS_FILE}) + if ! gcloud auth list | grep -E "\*\s+${sa_email}" + then + gcloud auth activate-service-account --key-file="${GCP_SHARED_CREDENTIALS_FILE}" + gcloud config set project "${GOOGLE_PROJECT_ID}" + fi fi fi @@ -100,7 +106,7 @@ REGION="${LEASED_RESOURCE}" echo "Using region: ${REGION}" ZONE_0=$(gcloud compute regions describe ${REGION} --format=json | jq -r .zones[0] | cut -d "/" -f9) -MACHINE_TYPE="n2-standard-2" +MACHINE_TYPE="${BASTION_MACHINE_TYPE:-n2-standard-2}" ##################################### ##########Create Bastion############# diff --git a/ci-operator/step-registry/gcp/provision/bastionhost/gcp-provision-bastionhost-ref.yaml b/ci-operator/step-registry/gcp/provision/bastionhost/gcp-provision-bastionhost-ref.yaml index adb96b6673aba..3c8d03fe35624 100644 --- a/ci-operator/step-registry/gcp/provision/bastionhost/gcp-provision-bastionhost-ref.yaml +++ b/ci-operator/step-registry/gcp/provision/bastionhost/gcp-provision-bastionhost-ref.yaml @@ -29,5 +29,20 @@ ref: - name: OSD_QE_PROJECT_AS_SERVICE_PROJECT default: "no" documentation: Whether to use OSD QE's project as the service project. The supported values are [no, yes]. + - name: BASTION_MACHINE_TYPE + default: "" + documentation: |- + The GCP machine type for the bastion host. If empty, defaults to + n2-standard-2. + - name: BASTION_IMAGE_NAME + default: "" + documentation: |- + The GCP image name for the bastion host. If empty, defaults to a + Fedora CoreOS image. + - name: BASTION_IMAGE_PROJECT + default: "" + documentation: |- + The GCP project hosting the bastion image. If empty, defaults to + fedora-coreos-cloud. documentation: |- The step launches Linux bastion host within the CONTROL_PLANE_SUBNET. diff --git a/ci-operator/step-registry/gcp/provision/vpc/gcp-provision-vpc-commands.sh b/ci-operator/step-registry/gcp/provision/vpc/gcp-provision-vpc-commands.sh index fdbee1363f70d..4ee38c4155ea0 100755 --- a/ci-operator/step-registry/gcp/provision/vpc/gcp-provision-vpc-commands.sh +++ b/ci-operator/step-registry/gcp/provision/vpc/gcp-provision-vpc-commands.sh @@ -73,11 +73,17 @@ EOF GOOGLE_PROJECT_ID="$(< ${CLUSTER_PROFILE_DIR}/openshift_gcp_project)" export GCP_SHARED_CREDENTIALS_FILE="${CLUSTER_PROFILE_DIR}/gce.json" -sa_email=$(jq -r .client_email ${GCP_SHARED_CREDENTIALS_FILE}) -if ! gcloud auth list | grep -E "\*\s+${sa_email}" -then - gcloud auth activate-service-account --key-file="${GCP_SHARED_CREDENTIALS_FILE}" +CRED_TYPE=$(jq -r .type "${GCP_SHARED_CREDENTIALS_FILE}" 2>/dev/null) +if [[ "${CRED_TYPE}" == "external_account" ]]; then + gcloud auth login --cred-file="${GCP_SHARED_CREDENTIALS_FILE}" --quiet gcloud config set project "${GOOGLE_PROJECT_ID}" +else + sa_email=$(jq -r .client_email ${GCP_SHARED_CREDENTIALS_FILE}) + if ! gcloud auth list | grep -E "\*\s+${sa_email}" + then + gcloud auth activate-service-account --key-file="${GCP_SHARED_CREDENTIALS_FILE}" + gcloud config set project "${GOOGLE_PROJECT_ID}" + fi fi echo "$(date -u --rfc-3339=seconds) - Copying resource files from lib dir..." diff --git a/ci-operator/step-registry/ipi/conf/gcd/OWNERS b/ci-operator/step-registry/ipi/conf/gcd/OWNERS new file mode 100644 index 0000000000000..d93971ab2936d --- /dev/null +++ b/ci-operator/step-registry/ipi/conf/gcd/OWNERS @@ -0,0 +1,5 @@ +approvers: +- barbacbd +- patrickdillon +- rochacbruno +- tthvo diff --git a/ci-operator/step-registry/ipi/conf/gcd/README.md b/ci-operator/step-registry/ipi/conf/gcd/README.md new file mode 100644 index 0000000000000..12f6e72509e36 --- /dev/null +++ b/ci-operator/step-registry/ipi/conf/gcd/README.md @@ -0,0 +1,150 @@ +# Google Cloud Dedicated (GCD) CI Configuration + +This directory contains step-registry components for running OpenShift +installer e2e tests on Google Cloud Dedicated. + +## Prerequisites + +1. A GCD project with a Workload Identity Federation (WIF) pool configured + to trust the CI build cluster's AWS account via STS role chaining. +2. A GCD service account with installer-required IAM roles (compute, DNS, + storage, IAM, etc.). +3. The WIF pool must have a provider with an attribute condition that + matches the CI pod's AWS identity. +4. The DPTP team must add the `home_role_arn` to the build cluster's + `aws-sts-cluster-config` secret so that STS is activated for the + `gcd` cluster profile. + +## Authentication Flow + +GCD CI jobs use AWS STS role chaining to authenticate: + +``` +CI pod (build cluster) + -> AWS STS token (projected SA token) + -> home role (build cluster AWS account) + -> hub role (shared hub AWS account, trusted by GCD WIF) + -> GCD STS exchanges AWS credentials for GCD access token + -> impersonates GCD service account +``` + +ci-operator automatically injects the AWS config file and STS token when +the cluster profile secret contains `home_role_arn`, `hub_role_arn`, and +`target_role_arn`. + +The job must set `AWS_PROFILE=hub` because GCD WIF trusts only the hub +account. The `default` profile points to a different target account. + +## Cluster Profile Secret (`cluster-secrets-gcd`) + +The secret is stored in the CI vault at +`selfservice.vault.ci.openshift.org` and must contain: + +| Key | Description | +|-----|-------------| +| `gce.json` | WIF credential config (see format below) | +| `openshift_gcp_project` | GCD project ID with prefix, e.g. `eu0:openshift` | +| `public_hosted_zone` | Base domain for DNS records | +| `ssh-publickey` | SSH public key for node access | +| `ssh-privatekey` | SSH private key for node access | +| `home_role_arn` | ARN of the home IAM role on the build cluster account | +| `hub_role_arn` | ARN of the hub IAM role (trusted by GCD WIF) | +| `target_role_arn` | ARN of the target IAM role | + +The vault secret must also have these metadata keys for sync: +``` +secretsync/target-namespace: ci +secretsync/target-name: cluster-secrets-gcd +``` + +## WIF Credential Config Format (`gce.json`) + +The credential config is an `external_account` type JSON that tells the +Google SDK how to exchange AWS credentials for GCD tokens: + +```json +{ + "universe_domain": "apis-berlin-build0.goog", + "type": "external_account", + "audience": "//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID", + "subject_token_type": "urn:ietf:params:aws:token-type:aws4_request", + "token_url": "https://sts.apis-berlin-build0.goog/v1/token", + "credential_source": { + "environment_id": "aws1", + "region_url": "http://169.254.169.254/latest/meta-data/placement/availability-zone", + "url": "http://169.254.169.254/latest/meta-data/iam/security-credentials", + "regional_cred_verification_url": "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15" + }, + "service_account_impersonation_url": "https://iamcredentials.apis-berlin-build0.goog/v1/projects/-/serviceAccounts/SA_EMAIL:generateAccessToken" +} +``` + +Replace `PROJECT_NUMBER`, `POOL_ID`, `PROVIDER_ID`, and `SA_EMAIL` with +actual values from your GCD WIF setup. + +### Generating the credential config + +```bash +export GOOGLE_CLOUD_UNIVERSE_DOMAIN=apis-berlin-build0.goog + +gcloud iam workload-identity-pools create-cred-config \ + "projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID" \ + --service-account=SA_EMAIL \ + --aws \ + --output-file=gce.json +``` + +After generating, verify that the `token_url` and +`service_account_impersonation_url` use `apis-berlin-build0.goog` +endpoints (not `googleapis.com`). The `gcloud` command may default to +public GCP endpoints, and the GCD endpoints must be set manually if so. + +## Environment Variables + +| Variable | Value | Purpose | +|----------|-------|---------| +| `AWS_PROFILE` | `hub` | Select the hub AWS profile trusted by GCD WIF | +| `GOOGLE_CLOUD_UNIVERSE_DOMAIN` | `apis-berlin-build0.goog` | Route API calls to GCD endpoints | +| `FEATURE_SET` | `TechPreviewNoUpgrade` | GCD support is currently TechPreview | +| `PUBLISH` | `Internal` | GCD only supports private DNS zones | +| `DEFAULT_MACHINE_OSIMAGE` | `rhcos10` | RHCOS image published to GCD | + +## Troubleshooting + +### "STS will not be activated" + +``` +STS hub and target role ARNs are set for profile gcd but home_role_arn +was not found in Secret aws-sts-cluster-config +``` + +The DPTP team needs to add the GCD profile's home role ARN to the build +cluster's `aws-sts-cluster-config` secret. Contact them with the ARN +values from the cluster profile secret. + +### "Connection refused" to 169.254.169.254 + +``` +HTTPConnectionPool(host='169.254.169.254', port=80): Max retries exceeded +``` + +The Google SDK is trying to get AWS credentials from the EC2 metadata +service, which means STS was not activated (see above). Once STS is +active, ci-operator injects AWS credentials as environment variables and +the SDK uses those instead of IMDS. + +### "external_account" type validation failure + +The `ipi-conf-gcd-wif-auth` step validates that `gce.json` contains a +valid `external_account` credential config. If this fails, check: +- The file was uploaded to vault correctly (valid JSON) +- The `type` field is `external_account` +- The vault secret has synced (can take up to 30 minutes) + +### gcloud auth succeeds but API calls fail + +If `gcloud auth login --cred-file` reports success but subsequent commands +fail, check: +- `GOOGLE_CLOUD_UNIVERSE_DOMAIN` is set to `apis-berlin-build0.goog` +- The `token_url` in `gce.json` uses `sts.apis-berlin-build0.goog` +- The GCD service account has the required IAM roles diff --git a/ci-operator/step-registry/ipi/conf/gcd/ipi-conf-gcd-chain.metadata.json b/ci-operator/step-registry/ipi/conf/gcd/ipi-conf-gcd-chain.metadata.json new file mode 100644 index 0000000000000..acc7086049d1e --- /dev/null +++ b/ci-operator/step-registry/ipi/conf/gcd/ipi-conf-gcd-chain.metadata.json @@ -0,0 +1,11 @@ +{ + "path": "ipi/conf/gcd/ipi-conf-gcd-chain.yaml", + "owners": { + "approvers": [ + "barbacbd", + "patrickdillon", + "rochacbruno", + "tthvo" + ] + } +} \ No newline at end of file diff --git a/ci-operator/step-registry/ipi/conf/gcd/ipi-conf-gcd-chain.yaml b/ci-operator/step-registry/ipi/conf/gcd/ipi-conf-gcd-chain.yaml new file mode 100644 index 0000000000000..7cec3138e6d7f --- /dev/null +++ b/ci-operator/step-registry/ipi/conf/gcd/ipi-conf-gcd-chain.yaml @@ -0,0 +1,14 @@ +chain: + as: ipi-conf-gcd + steps: + - ref: ipi-conf + - ref: ipi-conf-telemetry + - ref: ipi-conf-gcd-wif-auth + - ref: ipi-conf-gcp + - ref: ipi-conf-gcp-osimage + - ref: ipi-conf-gcp-zones + - ref: ipi-install-monitoringpvc + documentation: >- + This chain generates an install-config.yaml configured to run clusters + on Google Cloud Dedicated (GCD). Validates the WIF credential config + before configuring GCP-specific install-config fields. diff --git a/ci-operator/step-registry/ipi/conf/gcd/wif-auth/OWNERS b/ci-operator/step-registry/ipi/conf/gcd/wif-auth/OWNERS new file mode 100644 index 0000000000000..d93971ab2936d --- /dev/null +++ b/ci-operator/step-registry/ipi/conf/gcd/wif-auth/OWNERS @@ -0,0 +1,5 @@ +approvers: +- barbacbd +- patrickdillon +- rochacbruno +- tthvo diff --git a/ci-operator/step-registry/ipi/conf/gcd/wif-auth/ipi-conf-gcd-wif-auth-commands.sh b/ci-operator/step-registry/ipi/conf/gcd/wif-auth/ipi-conf-gcd-wif-auth-commands.sh new file mode 100755 index 0000000000000..ebb71ca549063 --- /dev/null +++ b/ci-operator/step-registry/ipi/conf/gcd/wif-auth/ipi-conf-gcd-wif-auth-commands.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -euo pipefail + +export GOOGLE_CLOUD_UNIVERSE_DOMAIN="${GOOGLE_CLOUD_UNIVERSE_DOMAIN:-apis-berlin-build0.goog}" + +CRED_FILE="${CLUSTER_PROFILE_DIR}/gce.json" +if [[ ! -f "${CRED_FILE}" ]]; then + echo "ERROR: WIF credential config not found at ${CRED_FILE}" + exit 1 +fi + +if ! CRED_TYPE=$(jq -er '.type' "${CRED_FILE}" 2>/dev/null); then + echo "ERROR: Invalid WIF credential config at ${CRED_FILE}" + exit 1 +fi +if [[ "${CRED_TYPE}" != "external_account" ]]; then + echo "ERROR: Expected external_account credential type, got ${CRED_TYPE}" + exit 1 +fi + +echo "GCD WIF configuration validated" +echo " GOOGLE_CLOUD_UNIVERSE_DOMAIN=${GOOGLE_CLOUD_UNIVERSE_DOMAIN}" +echo " Credential type: ${CRED_TYPE}" diff --git a/ci-operator/step-registry/ipi/conf/gcd/wif-auth/ipi-conf-gcd-wif-auth-ref.metadata.json b/ci-operator/step-registry/ipi/conf/gcd/wif-auth/ipi-conf-gcd-wif-auth-ref.metadata.json new file mode 100644 index 0000000000000..8164c0db98284 --- /dev/null +++ b/ci-operator/step-registry/ipi/conf/gcd/wif-auth/ipi-conf-gcd-wif-auth-ref.metadata.json @@ -0,0 +1,11 @@ +{ + "path": "ipi/conf/gcd/wif-auth/ipi-conf-gcd-wif-auth-ref.yaml", + "owners": { + "approvers": [ + "barbacbd", + "patrickdillon", + "rochacbruno", + "tthvo" + ] + } +} \ No newline at end of file diff --git a/ci-operator/step-registry/ipi/conf/gcd/wif-auth/ipi-conf-gcd-wif-auth-ref.yaml b/ci-operator/step-registry/ipi/conf/gcd/wif-auth/ipi-conf-gcd-wif-auth-ref.yaml new file mode 100644 index 0000000000000..a0c5b05e6f72c --- /dev/null +++ b/ci-operator/step-registry/ipi/conf/gcd/wif-auth/ipi-conf-gcd-wif-auth-ref.yaml @@ -0,0 +1,25 @@ +ref: + as: ipi-conf-gcd-wif-auth + from_image: + namespace: ocp + name: "4.18" + tag: upi-installer + commands: ipi-conf-gcd-wif-auth-commands.sh + resources: + requests: + cpu: 10m + memory: 100Mi + env: + - name: AWS_PROFILE + default: "hub" + documentation: |- + The AWS profile to use for GCD WIF authentication. Must be "hub" + because GCD WIF trusts only the hub account. The default profile + points to a different target account and will fail the STS exchange. + - name: GOOGLE_CLOUD_UNIVERSE_DOMAIN + default: "apis-berlin-build0.goog" + documentation: |- + The universe domain for Google Cloud Dedicated. + documentation: |- + Validates the GCD WIF credential configuration and sets environment + variables required for Google Cloud Dedicated authentication. diff --git a/ci-operator/step-registry/ipi/conf/gcp/zones/ipi-conf-gcp-zones-commands.sh b/ci-operator/step-registry/ipi/conf/gcp/zones/ipi-conf-gcp-zones-commands.sh index be4b9490ea5b0..8ff5b80587970 100755 --- a/ci-operator/step-registry/ipi/conf/gcp/zones/ipi-conf-gcp-zones-commands.sh +++ b/ci-operator/step-registry/ipi/conf/gcp/zones/ipi-conf-gcp-zones-commands.sh @@ -107,12 +107,19 @@ function array_intersection_or_fallback() { } export GCP_SHARED_CREDENTIALS_FILE="${CLUSTER_PROFILE_DIR}/gce.json" -GOOGLE_PROJECT_ID=$(jq -r .project_id ${GCP_SHARED_CREDENTIALS_FILE}) -sa_email=$(jq -r .client_email ${GCP_SHARED_CREDENTIALS_FILE}) -if ! gcloud auth list | grep -E "\*\s+${sa_email}" -then - gcloud auth activate-service-account --key-file="${GCP_SHARED_CREDENTIALS_FILE}" +CRED_TYPE=$(jq -r .type "${GCP_SHARED_CREDENTIALS_FILE}") +if [[ "${CRED_TYPE}" == "external_account" ]]; then + GOOGLE_PROJECT_ID="$(< ${CLUSTER_PROFILE_DIR}/openshift_gcp_project)" + gcloud auth login --cred-file="${GCP_SHARED_CREDENTIALS_FILE}" --quiet gcloud config set project "${GOOGLE_PROJECT_ID}" +else + GOOGLE_PROJECT_ID=$(jq -r .project_id ${GCP_SHARED_CREDENTIALS_FILE}) + sa_email=$(jq -r .client_email ${GCP_SHARED_CREDENTIALS_FILE}) + if ! gcloud auth list | grep -E "\*\s+${sa_email}" + then + gcloud auth activate-service-account --key-file="${GCP_SHARED_CREDENTIALS_FILE}" + gcloud config set project "${GOOGLE_PROJECT_ID}" + fi fi # As a temporary workaround of https://redhat.atlassian.net/browse/OCPBUGS-78431, diff --git a/ci-operator/step-registry/ipi/deprovision/deprovision/ipi-deprovision-deprovision-commands.sh b/ci-operator/step-registry/ipi/deprovision/deprovision/ipi-deprovision-deprovision-commands.sh index 805e30ed19ccd..1ded09cc215a1 100755 --- a/ci-operator/step-registry/ipi/deprovision/deprovision/ipi-deprovision-deprovision-commands.sh +++ b/ci-operator/step-registry/ipi/deprovision/deprovision/ipi-deprovision-deprovision-commands.sh @@ -48,7 +48,10 @@ fi export AZURE_AUTH_LOCATION=$CLUSTER_PROFILE_DIR/osServicePrincipal.json export GOOGLE_CLOUD_KEYFILE_JSON=$CLUSTER_PROFILE_DIR/gce.json -if [ -f "${SHARED_DIR}/gcp_min_permissions.json" ]; then +if [[ "$(jq -r .type ${GOOGLE_CLOUD_KEYFILE_JSON} 2>/dev/null)" == "external_account" ]]; then + echo "$(date -u --rfc-3339=seconds) - Using WIF external_account credentials for GCD..." + export GOOGLE_APPLICATION_CREDENTIALS="${GOOGLE_CLOUD_KEYFILE_JSON}" +elif [ -f "${SHARED_DIR}/gcp_min_permissions.json" ]; then echo "$(date -u --rfc-3339=seconds) - Using the IAM service account for the minimum permissions testing on GCP..." export GOOGLE_CLOUD_KEYFILE_JSON="${SHARED_DIR}/gcp_min_permissions.json" elif [ -f "${SHARED_DIR}/user_tags_sa.json" ]; then diff --git a/ci-operator/step-registry/ipi/gcd/OWNERS b/ci-operator/step-registry/ipi/gcd/OWNERS new file mode 100644 index 0000000000000..d93971ab2936d --- /dev/null +++ b/ci-operator/step-registry/ipi/gcd/OWNERS @@ -0,0 +1,5 @@ +approvers: +- barbacbd +- patrickdillon +- rochacbruno +- tthvo diff --git a/ci-operator/step-registry/ipi/gcd/pre/OWNERS b/ci-operator/step-registry/ipi/gcd/pre/OWNERS new file mode 100644 index 0000000000000..d93971ab2936d --- /dev/null +++ b/ci-operator/step-registry/ipi/gcd/pre/OWNERS @@ -0,0 +1,5 @@ +approvers: +- barbacbd +- patrickdillon +- rochacbruno +- tthvo diff --git a/ci-operator/step-registry/ipi/gcd/pre/ipi-gcd-pre-chain.metadata.json b/ci-operator/step-registry/ipi/gcd/pre/ipi-gcd-pre-chain.metadata.json new file mode 100644 index 0000000000000..ed396ccfe791e --- /dev/null +++ b/ci-operator/step-registry/ipi/gcd/pre/ipi-gcd-pre-chain.metadata.json @@ -0,0 +1,11 @@ +{ + "path": "ipi/gcd/pre/ipi-gcd-pre-chain.yaml", + "owners": { + "approvers": [ + "barbacbd", + "patrickdillon", + "rochacbruno", + "tthvo" + ] + } +} \ No newline at end of file diff --git a/ci-operator/step-registry/ipi/gcd/pre/ipi-gcd-pre-chain.yaml b/ci-operator/step-registry/ipi/gcd/pre/ipi-gcd-pre-chain.yaml new file mode 100644 index 0000000000000..b863316f3d2f1 --- /dev/null +++ b/ci-operator/step-registry/ipi/gcd/pre/ipi-gcd-pre-chain.yaml @@ -0,0 +1,14 @@ +chain: + as: ipi-gcd-pre + steps: + - ref: gcp-provision-vpc + - ref: ignition-bastionhost + - ref: gcp-provision-bastionhost + - ref: proxy-config-generate + - chain: ipi-conf-gcd + - ref: rhcos-conf-osstream + - chain: ipi-install + documentation: |- + The IPI GCD setup chain provisions a VPC, bastion host, and proxy, + then installs a private OpenShift cluster on Google Cloud Dedicated + using WIF authentication. diff --git a/ci-operator/step-registry/ipi/install/install/ipi-install-install-commands.sh b/ci-operator/step-registry/ipi/install/install/ipi-install-install-commands.sh index 59efc308a6983..646525ad2283b 100755 --- a/ci-operator/step-registry/ipi/install/install/ipi-install-install-commands.sh +++ b/ci-operator/step-registry/ipi/install/install/ipi-install-install-commands.sh @@ -681,7 +681,10 @@ azurestack) ;; gcp) export GOOGLE_CLOUD_KEYFILE_JSON=${CLUSTER_PROFILE_DIR}/gce.json - if [ -f "${SHARED_DIR}/gcp_min_permissions.json" ]; then + if [[ "$(jq -r .type ${GOOGLE_CLOUD_KEYFILE_JSON} 2>/dev/null)" == "external_account" ]]; then + echo "$(date -u --rfc-3339=seconds) - Using WIF external_account credentials for GCD..." + export GOOGLE_APPLICATION_CREDENTIALS="${GOOGLE_CLOUD_KEYFILE_JSON}" + elif [ -f "${SHARED_DIR}/gcp_min_permissions.json" ]; then echo "$(date -u --rfc-3339=seconds) - Using the IAM service account for the minimum permissions testing on GCP..." export GOOGLE_CLOUD_KEYFILE_JSON="${SHARED_DIR}/gcp_min_permissions.json" elif [ -f "${SHARED_DIR}/user_tags_sa.json" ]; then diff --git a/ci-operator/step-registry/openshift/e2e/gcd/OWNERS b/ci-operator/step-registry/openshift/e2e/gcd/OWNERS new file mode 100644 index 0000000000000..d93971ab2936d --- /dev/null +++ b/ci-operator/step-registry/openshift/e2e/gcd/OWNERS @@ -0,0 +1,5 @@ +approvers: +- barbacbd +- patrickdillon +- rochacbruno +- tthvo diff --git a/ci-operator/step-registry/openshift/e2e/gcd/openshift-e2e-gcd-workflow.metadata.json b/ci-operator/step-registry/openshift/e2e/gcd/openshift-e2e-gcd-workflow.metadata.json new file mode 100644 index 0000000000000..093a7d2826c14 --- /dev/null +++ b/ci-operator/step-registry/openshift/e2e/gcd/openshift-e2e-gcd-workflow.metadata.json @@ -0,0 +1,11 @@ +{ + "path": "openshift/e2e/gcd/openshift-e2e-gcd-workflow.yaml", + "owners": { + "approvers": [ + "barbacbd", + "patrickdillon", + "rochacbruno", + "tthvo" + ] + } +} \ No newline at end of file diff --git a/ci-operator/step-registry/openshift/e2e/gcd/openshift-e2e-gcd-workflow.yaml b/ci-operator/step-registry/openshift/e2e/gcd/openshift-e2e-gcd-workflow.yaml new file mode 100644 index 0000000000000..d1d96048921bc --- /dev/null +++ b/ci-operator/step-registry/openshift/e2e/gcd/openshift-e2e-gcd-workflow.yaml @@ -0,0 +1,17 @@ +workflow: + as: openshift-e2e-gcd + steps: + allow_best_effort_post_steps: true + pre: + - chain: ipi-gcd-pre + test: + - ref: openshift-e2e-test + post: + - chain: gather-core-dump + - chain: ipi-gcp-post + - ref: gcp-deprovision-bastionhost + - ref: gcp-deprovision-vpc + documentation: |- + The OpenShift E2E GCD workflow executes the common end-to-end test + suite on a private cluster on Google Cloud Dedicated with WIF + authentication.