diff --git a/ci-operator/config/openshift/hypershift/openshift-hypershift-main.yaml b/ci-operator/config/openshift/hypershift/openshift-hypershift-main.yaml
index 2c19fe8862c57..8837995a9cb79 100644
--- a/ci-operator/config/openshift/hypershift/openshift-hypershift-main.yaml
+++ b/ci-operator/config/openshift/hypershift/openshift-hypershift-main.yaml
@@ -511,6 +511,18 @@ tests:
GKE_REGION: us-central1
GKE_RELEASE_CHANNEL: stable
workflow: hypershift-gcp-gke-e2e
+- always_run: false
+ as: e2e-v2-gke
+ capabilities:
+ - build-tmpfs
+ optional: true
+ run_if_changed: ^(api/hypershift/v1beta1/gcp.*|hypershift-operator/controllers/.*/gcp.*|control-plane-operator/controllers/.*/gcp.*|cmd/cluster/gcp/.*|cmd/nodepool/gcp/.*)
+ steps:
+ cluster_profile: hypershift-gcp
+ env:
+ GKE_REGION: us-central1
+ GKE_RELEASE_CHANNEL: stable
+ workflow: hypershift-gcp-gke-e2e-v2
zz_generated_metadata:
branch: main
org: openshift
diff --git a/ci-operator/jobs/openshift/hypershift/openshift-hypershift-main-presubmits.yaml b/ci-operator/jobs/openshift/hypershift/openshift-hypershift-main-presubmits.yaml
index 30e17a950e4bc..78aff6632a2bf 100644
--- a/ci-operator/jobs/openshift/hypershift/openshift-hypershift-main-presubmits.yaml
+++ b/ci-operator/jobs/openshift/hypershift/openshift-hypershift-main-presubmits.yaml
@@ -1474,7 +1474,7 @@ presubmits:
branches:
- ^main$
- ^main-
- cluster: build03
+ cluster: build05
context: ci/prow/e2e-kubevirt-aws-ovn
decorate: true
labels:
@@ -2281,6 +2281,89 @@ presubmits:
secret:
secretName: result-aggregator
trigger: (?m)^/test( | .* )e2e-v2-aws-backuprestore,?($|\s.*)
+ - agent: kubernetes
+ always_run: false
+ branches:
+ - ^main$
+ - ^main-
+ cluster: build01
+ context: ci/prow/e2e-v2-gke
+ decorate: true
+ labels:
+ capability/build-tmpfs: build-tmpfs
+ ci-operator.openshift.io/cloud: hypershift-gcp
+ ci-operator.openshift.io/cloud-cluster-profile: hypershift-gcp
+ ci.openshift.io/generator: prowgen
+ pj-rehearse.openshift.io/can-be-rehearsed: "true"
+ name: pull-ci-openshift-hypershift-main-e2e-v2-gke
+ optional: true
+ rerun_command: /test e2e-v2-gke
+ run_if_changed: ^(api/hypershift/v1beta1/gcp.*|hypershift-operator/controllers/.*/gcp.*|control-plane-operator/controllers/.*/gcp.*|cmd/cluster/gcp/.*|cmd/nodepool/gcp/.*)
+ 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-v2-gke
+ 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-v2-gke,?($|\s.*)
- agent: kubernetes
always_run: true
branches:
@@ -2342,7 +2425,7 @@ presubmits:
branches:
- ^main$
- ^main-
- cluster: build03
+ cluster: build05
context: ci/prow/okd-scos-e2e-aws-ovn
decorate: true
decoration_config:
diff --git a/ci-operator/step-registry/hypershift/gcp/create/OWNERS b/ci-operator/step-registry/hypershift/gcp/create/OWNERS
new file mode 100644
index 0000000000000..e6bc6d6200e48
--- /dev/null
+++ b/ci-operator/step-registry/hypershift/gcp/create/OWNERS
@@ -0,0 +1,20 @@
+approvers:
+- csrwng
+- muraee
+- devguyio
+- apahim
+- cblecker
+- ckandag
+- cristianoveiga
+- jimdaga
+- patjlm
+reviewers:
+- csrwng
+- muraee
+- devguyio
+- apahim
+- cblecker
+- ckandag
+- cristianoveiga
+- jimdaga
+- patjlm
diff --git a/ci-operator/step-registry/hypershift/gcp/create/hypershift-gcp-create-chain.metadata.json b/ci-operator/step-registry/hypershift/gcp/create/hypershift-gcp-create-chain.metadata.json
new file mode 100644
index 0000000000000..7a90881739590
--- /dev/null
+++ b/ci-operator/step-registry/hypershift/gcp/create/hypershift-gcp-create-chain.metadata.json
@@ -0,0 +1,27 @@
+{
+ "path": "hypershift/gcp/create/hypershift-gcp-create-chain.yaml",
+ "owners": {
+ "approvers": [
+ "csrwng",
+ "muraee",
+ "devguyio",
+ "apahim",
+ "cblecker",
+ "ckandag",
+ "cristianoveiga",
+ "jimdaga",
+ "patjlm"
+ ],
+ "reviewers": [
+ "csrwng",
+ "muraee",
+ "devguyio",
+ "apahim",
+ "cblecker",
+ "ckandag",
+ "cristianoveiga",
+ "jimdaga",
+ "patjlm"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/ci-operator/step-registry/hypershift/gcp/create/hypershift-gcp-create-chain.yaml b/ci-operator/step-registry/hypershift/gcp/create/hypershift-gcp-create-chain.yaml
new file mode 100644
index 0000000000000..f9c0e2786970c
--- /dev/null
+++ b/ci-operator/step-registry/hypershift/gcp/create/hypershift-gcp-create-chain.yaml
@@ -0,0 +1,154 @@
+chain:
+ as: hypershift-gcp-create
+ steps:
+ - as: create-hostedcluster
+ cli: latest
+ env:
+ - name: HYPERSHIFT_NODE_COUNT
+ default: "2"
+ documentation: "Number of nodes for the hosted cluster NodePool."
+ - name: HYPERSHIFT_GCP_BOOT_IMAGE
+ default: ""
+ documentation: "GCP boot image for hosted cluster nodes (RHCOS image path). If empty, falls back to a pinned default (see TODO GCP-440)."
+ - name: HYPERSHIFT_GCP_CI_DNS_DOMAIN
+ default: ""
+ documentation: "DNS domain for HyperShift CI hosted clusters"
+ commands: |-
+ set -exuo pipefail
+
+ # Load configuration from SHARED_DIR (populated by prior steps)
+ GCP_REGION="$(<"${SHARED_DIR}/gcp-region")"
+ HC_PROJECT_ID="$(<"${SHARED_DIR}/hosted-cluster-project-id")"
+ CLUSTER_NAME="$(<"${SHARED_DIR}/hosted-cluster-name")"
+
+ # Network config (from hypershift-gcp-hosted-cluster-setup)
+ VPC_NAME="$(<"${SHARED_DIR}/hc-vpc-name")"
+ PSC_SUBNET="$(<"${SHARED_DIR}/hc-subnet-name")"
+
+ # WIF config (from hypershift-gcp-hosted-cluster-setup)
+ WIF_PROJECT_NUMBER="$(<"${SHARED_DIR}/wif-project-number")"
+ WIF_POOL_ID="$(<"${SHARED_DIR}/wif-pool-id")"
+ WIF_PROVIDER_ID="$(<"${SHARED_DIR}/wif-provider-id")"
+ CONTROLPLANE_SA="$(<"${SHARED_DIR}/controlplane-sa")"
+ NODEPOOL_SA="$(<"${SHARED_DIR}/nodepool-sa")"
+ CLOUDCONTROLLER_SA="$(<"${SHARED_DIR}/cloudcontroller-sa")"
+ STORAGE_SA="$(<"${SHARED_DIR}/storage-sa")"
+ IMAGEREGISTRY_SA="$(<"${SHARED_DIR}/imageregistry-sa")"
+ SA_SIGNING_KEY_PATH="$(<"${SHARED_DIR}/sa-signing-key-path")"
+
+ # Construct base domain and OIDC issuer URL
+ BASE_DOMAIN="in.${CLUSTER_NAME}.${HYPERSHIFT_GCP_CI_DNS_DOMAIN}"
+ OIDC_ISSUER_URL="https://hypershift-${CLUSTER_NAME}-oidc"
+
+ RELEASE_IMAGE="${OCP_IMAGE_LATEST}"
+
+ # TODO(GCP-426): Remove CAPG image override once HyperShift's CAPI CRDs serve v1beta2
+ CAPG_IMAGE="quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:bdec420448b81cc57f5b53bbcf491c0ed53b6e3ca97da722f69f386a373afe50"
+
+ # TODO(GCP-440): Remove boot image pin once NodePool controller discovers it from stream metadata
+ GCP_BOOT_IMAGE="${HYPERSHIFT_GCP_BOOT_IMAGE:-projects/rhcos-cloud/global/images/rhcos-9-6-20250925-0-gcp-x86-64}"
+
+ HC_NAME="gcp-hc-${CLUSTER_NAME:0-8}"
+ echo "$(date) Creating GCP HyperShift cluster ${HC_NAME}"
+
+ bin/hypershift create cluster gcp \
+ --name "${HC_NAME}" \
+ --namespace clusters \
+ --base-domain "${BASE_DOMAIN}" \
+ --external-dns-domain="${BASE_DOMAIN}" \
+ --node-pool-replicas "${HYPERSHIFT_NODE_COUNT}" \
+ --release-image "${RELEASE_IMAGE}" \
+ --pull-secret /etc/ci-pull-credentials/.dockerconfigjson \
+ --project "${HC_PROJECT_ID}" \
+ --region "${GCP_REGION}" \
+ --network "${VPC_NAME}" \
+ --private-service-connect-subnet "${PSC_SUBNET}" \
+ --endpoint-access PublicAndPrivate \
+ --workload-identity-project-number "${WIF_PROJECT_NUMBER}" \
+ --workload-identity-pool-id "${WIF_POOL_ID}" \
+ --workload-identity-provider-id "${WIF_PROVIDER_ID}" \
+ --control-plane-service-account "${CONTROLPLANE_SA}" \
+ --node-pool-service-account "${NODEPOOL_SA}" \
+ --cloud-controller-service-account "${CLOUDCONTROLLER_SA}" \
+ --storage-service-account "${STORAGE_SA}" \
+ --image-registry-service-account "${IMAGEREGISTRY_SA}" \
+ --service-account-signing-key-path "${SA_SIGNING_KEY_PATH}" \
+ --oidc-issuer-url "${OIDC_ISSUER_URL}" \
+ --boot-image "${GCP_BOOT_IMAGE}" \
+ --annotations "hypershift.openshift.io/capi-provider-gcp-image=${CAPG_IMAGE}" \
+ --annotations "hypershift.openshift.io/pod-security-admission-label-override=baseline" \
+ --disable-cluster-capabilities ImageRegistry \
+ --disable-cluster-capabilities Console \
+ --disable-cluster-capabilities Ingress \
+ --feature-set TechPreviewNoUpgrade
+
+ # Save HC CR name early so destroy chain can clean up if rollout times out
+ echo "${HC_NAME}" > "${SHARED_DIR}/cluster-name"
+
+ echo "Waiting for cluster to become available"
+ oc wait --timeout=30m --for=condition=Available --namespace=clusters "hostedcluster/${HC_NAME}"
+ echo "Cluster became available, creating kubeconfig"
+ bin/hypershift create kubeconfig --namespace=clusters --name="${HC_NAME}" > "${SHARED_DIR}/nested_kubeconfig"
+
+ # Wait for version rollout to complete
+ set +e
+ export HC_NAME
+ timeout 25m bash -c '
+ until [[ "$(oc get -n clusters hostedcluster/${HC_NAME} -o jsonpath='"'"'{.status.version.history[?(@.state!="")].state}'"'"')" = "Completed" ]]; do
+ sleep 15
+ done
+ '
+
+ if [[ $? -ne 0 ]]; then
+ cat << EOF > "${ARTIFACT_DIR}/junit_hosted_cluster.xml"
+
+
+
+
+
+
+
+
+ EOF
+ exit 1
+ else
+ cat << EOF > "${ARTIFACT_DIR}/junit_hosted_cluster.xml"
+
+
+
+
+
+
+
+
+ EOF
+ fi
+ from: hypershift-operator
+ grace_period: 5m0s
+ resources:
+ requests:
+ cpu: 100m
+ memory: 100Mi
+ timeout: 60m0s
+ credentials:
+ - mount_path: /etc/ci-pull-credentials
+ name: ci-pull-credentials
+ namespace: test-credentials
+ dependencies:
+ - name: "release:latest"
+ env: OCP_IMAGE_LATEST
+ documentation: |-
+ Creates a GCP HostedCluster using the hypershift CLI and waits for
+ the cluster version rollout to complete.
+
+ Reads infrastructure configuration from SHARED_DIR files created by
+ hypershift-gcp-gke-provision and hypershift-gcp-hosted-cluster-setup.
+
+ Saves the HostedCluster CR name to SHARED_DIR/cluster-name for the
+ e2e-v2 test chain and destroy chain.
diff --git a/ci-operator/step-registry/hypershift/gcp/destroy/OWNERS b/ci-operator/step-registry/hypershift/gcp/destroy/OWNERS
new file mode 100644
index 0000000000000..e6bc6d6200e48
--- /dev/null
+++ b/ci-operator/step-registry/hypershift/gcp/destroy/OWNERS
@@ -0,0 +1,20 @@
+approvers:
+- csrwng
+- muraee
+- devguyio
+- apahim
+- cblecker
+- ckandag
+- cristianoveiga
+- jimdaga
+- patjlm
+reviewers:
+- csrwng
+- muraee
+- devguyio
+- apahim
+- cblecker
+- ckandag
+- cristianoveiga
+- jimdaga
+- patjlm
diff --git a/ci-operator/step-registry/hypershift/gcp/destroy/hypershift-gcp-destroy-chain.metadata.json b/ci-operator/step-registry/hypershift/gcp/destroy/hypershift-gcp-destroy-chain.metadata.json
new file mode 100644
index 0000000000000..d856f3938e867
--- /dev/null
+++ b/ci-operator/step-registry/hypershift/gcp/destroy/hypershift-gcp-destroy-chain.metadata.json
@@ -0,0 +1,27 @@
+{
+ "path": "hypershift/gcp/destroy/hypershift-gcp-destroy-chain.yaml",
+ "owners": {
+ "approvers": [
+ "csrwng",
+ "muraee",
+ "devguyio",
+ "apahim",
+ "cblecker",
+ "ckandag",
+ "cristianoveiga",
+ "jimdaga",
+ "patjlm"
+ ],
+ "reviewers": [
+ "csrwng",
+ "muraee",
+ "devguyio",
+ "apahim",
+ "cblecker",
+ "ckandag",
+ "cristianoveiga",
+ "jimdaga",
+ "patjlm"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/ci-operator/step-registry/hypershift/gcp/destroy/hypershift-gcp-destroy-chain.yaml b/ci-operator/step-registry/hypershift/gcp/destroy/hypershift-gcp-destroy-chain.yaml
new file mode 100644
index 0000000000000..998e82ff27557
--- /dev/null
+++ b/ci-operator/step-registry/hypershift/gcp/destroy/hypershift-gcp-destroy-chain.yaml
@@ -0,0 +1,36 @@
+chain:
+ as: hypershift-gcp-destroy
+ steps:
+ - as: destroy
+ cli: latest
+ commands: |-
+ set -exuo pipefail
+
+ if [[ ! -f "${SHARED_DIR}/cluster-name" ]]; then
+ echo "WARNING: cluster-name not found — create step may not have completed. Skipping destroy."
+ exit 0
+ fi
+
+ CLUSTER_NAME="$(<"${SHARED_DIR}/cluster-name")"
+ echo "$(date) Deleting GCP HyperShift cluster ${CLUSTER_NAME}"
+ bin/hypershift destroy cluster gcp \
+ --name "${CLUSTER_NAME}" \
+ --namespace clusters \
+ --cluster-grace-period 40m
+ echo "$(date) Finished deleting cluster"
+ from: hypershift-operator
+ grace_period: 5m0s
+ resources:
+ requests:
+ cpu: 100m
+ memory: 100Mi
+ timeout: 45m0s
+ best_effort: true
+ documentation: |-
+ Destroys a GCP HostedCluster by deleting the HostedCluster CR and
+ waiting for finalizers to complete.
+
+ The underlying GCP infrastructure (VPC, WIF, projects) is cleaned up
+ separately by hypershift-gcp-gke-deprovision via project deletion.
+
+ Reads cluster-name from SHARED_DIR (saved by hypershift-gcp-create).
diff --git a/ci-operator/step-registry/hypershift/gcp/gke/e2e-v2/OWNERS b/ci-operator/step-registry/hypershift/gcp/gke/e2e-v2/OWNERS
new file mode 100644
index 0000000000000..e6bc6d6200e48
--- /dev/null
+++ b/ci-operator/step-registry/hypershift/gcp/gke/e2e-v2/OWNERS
@@ -0,0 +1,20 @@
+approvers:
+- csrwng
+- muraee
+- devguyio
+- apahim
+- cblecker
+- ckandag
+- cristianoveiga
+- jimdaga
+- patjlm
+reviewers:
+- csrwng
+- muraee
+- devguyio
+- apahim
+- cblecker
+- ckandag
+- cristianoveiga
+- jimdaga
+- patjlm
diff --git a/ci-operator/step-registry/hypershift/gcp/gke/e2e-v2/hypershift-gcp-gke-e2e-v2-workflow.metadata.json b/ci-operator/step-registry/hypershift/gcp/gke/e2e-v2/hypershift-gcp-gke-e2e-v2-workflow.metadata.json
new file mode 100644
index 0000000000000..6887b63665934
--- /dev/null
+++ b/ci-operator/step-registry/hypershift/gcp/gke/e2e-v2/hypershift-gcp-gke-e2e-v2-workflow.metadata.json
@@ -0,0 +1,27 @@
+{
+ "path": "hypershift/gcp/gke/e2e-v2/hypershift-gcp-gke-e2e-v2-workflow.yaml",
+ "owners": {
+ "approvers": [
+ "csrwng",
+ "muraee",
+ "devguyio",
+ "apahim",
+ "cblecker",
+ "ckandag",
+ "cristianoveiga",
+ "jimdaga",
+ "patjlm"
+ ],
+ "reviewers": [
+ "csrwng",
+ "muraee",
+ "devguyio",
+ "apahim",
+ "cblecker",
+ "ckandag",
+ "cristianoveiga",
+ "jimdaga",
+ "patjlm"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/ci-operator/step-registry/hypershift/gcp/gke/e2e-v2/hypershift-gcp-gke-e2e-v2-workflow.yaml b/ci-operator/step-registry/hypershift/gcp/gke/e2e-v2/hypershift-gcp-gke-e2e-v2-workflow.yaml
new file mode 100644
index 0000000000000..cabe290d5b77b
--- /dev/null
+++ b/ci-operator/step-registry/hypershift/gcp/gke/e2e-v2/hypershift-gcp-gke-e2e-v2-workflow.yaml
@@ -0,0 +1,45 @@
+workflow:
+ as: hypershift-gcp-gke-e2e-v2
+ documentation: |-
+ The HyperShift GCP GKE e2e-v2 workflow provisions a GKE cluster,
+ creates a GCP hosted cluster, runs Ginkgo v2 tests, and cleans up.
+
+ Pre phase:
+ 1. ipi-install-rbac: Grant image-puller permissions on build farm
+ 2. hypershift-gcp-gke-provision: Create GCP projects, VPC, and GKE cluster
+ 3. hypershift-gcp-gke-prerequisites: Install CRDs and cert-manager on GKE
+ 4. hypershift-install: Install the HyperShift operator
+ 5. hypershift-gcp-control-plane-setup: Configure GCP Workload Identity for PSC and ExternalDNS
+ 6. hypershift-gcp-hosted-cluster-setup: Create WIF and network infrastructure
+ 7. hypershift-gcp-create: Create GCP HostedCluster and wait for rollout
+
+ Test phase:
+ 1. hypershift-e2e-v2: Run Ginkgo v2 tests against the hosted cluster
+
+ Post phase:
+ 1. hypershift-dump: Collect HyperShift artifacts for debugging
+ 2. hypershift-gcp-destroy: Destroy the GCP HostedCluster
+ 3. hypershift-gcp-gke-deprovision: Delete GKE cluster and GCP projects
+ steps:
+ pre:
+ - ref: ipi-install-rbac
+ - ref: hypershift-gcp-gke-provision
+ - ref: hypershift-gcp-gke-prerequisites
+ - ref: hypershift-install
+ - ref: hypershift-gcp-control-plane-setup
+ - ref: hypershift-gcp-hosted-cluster-setup
+ - chain: hypershift-gcp-create
+ test:
+ - chain: hypershift-e2e-v2
+ post:
+ - chain: hypershift-dump
+ - chain: hypershift-gcp-destroy
+ - ref: hypershift-gcp-gke-deprovision
+ env:
+ CLOUD_PROVIDER: "GCP"
+ GKE_REGION: "us-central1"
+ GKE_RELEASE_CHANNEL: "stable"
+ TECH_PREVIEW_NO_UPGRADE: "true"
+ HYPERSHIFT_GCP_CI_PROJECT: "gcp-hcp-hypershift-ci"
+ HYPERSHIFT_GCP_CI_DNS_ZONE: "hypershift-ci-gcp-hcp-openshiftapps-com"
+ HYPERSHIFT_GCP_CI_DNS_DOMAIN: "hypershift-ci.gcp-hcp.openshiftapps.com"