apiVersion: tekton.dev/v1 kind: Task metadata: name: provision-ephemeral-cluster spec: description: >- Provision an ephemeral cluster by leveraging the Test Platform (OpenShift CI) infrastructure. params: - name: ownerKind type: string default: PipelineRun description: >- The type of resource that should own the generated ephemeral cluster claim. Supported values: `PipelineRun`, `TaskRun`. - name: ownerName type: string description: >- The name of the resource that should own the ephemeral cluster claim. This should either be passed the value of `$(context.pipelineRun.name)` or `$(context.taskRun.name)` depending on the value of `ownerKind`. - name: ownerUid type: string description: >- The uid of the resource that should own the ephemeral cluster claim. This should either be passed the value of `$(context.pipelineRun.uid)` or `$(context.taskRun.uid)` depending on the value of `ownerKind`. - name: buildRoot type: string default: '{"image_stream_tag":{"name":"release","namespace":"openshift","tag":"rhel-9-release-golang-1.24-openshift-4.20"}}' description: >- This is going to be used as a `build_root` for the resulting ci-operator configuration. See https://docs.ci.openshift.org/docs/architecture/ci-operator/ for more information. This parameter is a string that is expected to be a valid JSON object. As an example: { "image_stream_tag": { "name": "release", "namespace": "openshift", "tag": "rhel-9-release-golang-1.24-openshift-4.20" } } - name: baseImages type: string default: '{}' description: >- This is going to be used as `base_images` for the resulting ci-operator configuration. See https://docs.ci.openshift.org/docs/architecture/ci-operator/ for more information. This parameter is a string that is expected to be a valid JSON object. As an example: { "ocp_4.20_base-rhel9": { "name": "4.20", "namespace": "ocp", "tag": "base-rhel9" } } - name: externalImages type: string default: '{}' description: >- This is going to be used as `external_images` for the resulting ci-operator configuration. See https://docs.ci.openshift.org/docs/architecture/ci-operator/ for more information. This parameter is a string that is expected to be a valid JSON object. - name: workflow type: string description: >- The workflow that will be used to provision the cluster. See https://steps.ci.openshift.org/#workflows for a complete list of any available workflow. - name: env type: string default: '{}' description: >- Environmental variables available in the OpenShift CI infrastructure at provisioning time. This parameter is expected to be a valid JSON object. What follow is an example that creates the "FOO" environment variable and assign the string "bar" to it: { "FOO": "bar" } The object structure is flat, no nested objects are allowed. The key-value pairs have to be strings. - name: clusterProfile type: string default: "" description: >- The cluster profile holds the IAM information for the account accountable for any resource created in a cloud provider. See https://docs.ci.openshift.org/docs/how-tos/adding-a-cluster-profile/ for more information. - name: clusterClaim type: string default: '{}' description: >- The claim specifictions to provision an cluster from Hive. See https://docs.ci.openshift.org/docs/how-tos/cluster-claim/ for more information. This parameter is a string that is expected to be a valid JSON object. As an example: { "architecture": "amd64", "as": "unused", "cloud": "aws", "labels": { "region": "us-east-1" }, "owner": "openshift-ci", "product": "ocp", "timeout": "1h0m0s", "version": "4.19" } - name: releases type: string default: '{}' description: >- The OpenShift release payload specification that will be installed on the ephemeral cluster. See https://docs.ci.openshift.org/docs/architecture/ci-operator/#describing-inclusion-in-an-openshift-release for more information. This parameter is a string that is expected to be a valid JSON object. As an example: { "initial": { "integration": { "name": "4.20", "namespace": "ocp" } }, "latest": { "integration": { "name": "4.20", "namespace": "ocp" } } } - name: resources type: string default: "" description: >- Set requests and limits on the containers involved in the ephemeral cluster provisioning procedure. See https://docs.ci.openshift.org/docs/architecture/ci-operator/ for more information. This parameter is a string that is expected to be a valid JSON object. Default values are defined as follow: { "*": { "requests": {"cpu": "200m"}, "limits": {"memory": "400Mi"}, } } - name: timeout type: string default: 2h description: "Wait for the ephemeral cluster to be ready until this timeout is reached" results: - name: secretRef description: Name of a Secret containing kubeconfig and password to access the provisioned ephemeral cluster. type: string - name: testPlatformClusterClaimName description: Name of the TestPlatformCluster Claim that is used to request an ephemeral cluster type: string - name: testPlatformClusterClaimNamespace description: Namespace of the TestPlatformCluster Claim that is used to request an ephemeral cluster type: string steps: - name: provision-ephemeral-cluster image: quay.io/konflux-ci/konflux-test:v1.4.52@sha256:aa2c97da9bb73a4e8d1c6b41950f8d902b74461be0e042debe89277fdc4ebe49 env: - name: BUILD_ROOT value: $(params.buildRoot) - name: BASE_IMAGES value: $(params.baseImages) - name: EXTERNAL_IMAGES value: $(params.externalImages) - name: WORKFLOW value: $(params.workflow) - name: ENV value: $(params.env) - name: CLUSTER_PROFILE value: $(params.clusterProfile) - name: CLUSTER_CLAIM value: $(params.clusterClaim) - name: RELEASES value: $(params.releases) - name: RESOURCES value: $(params.resources) - name: CLAIM_NAME value: $(context.taskRun.name) - name: NAMESPACE value: $(context.taskRun.namespace) - name: OWNER_KIND value: $(params.ownerKind) - name: OWNER_NAME value: $(params.ownerName) - name: OWNER_UID value: $(params.ownerUid) - name: SECRET value: $(context.taskRun.name) - name: CLUSTER_READY_TIMEOUT value: $(params.timeout) script: | #!/bin/bash set -eo pipefail # Use this to make sure the PJ URL is printed only once URL_DUMPED=0 TP_CLUSTER_NAME='' CLUSTER_READY=0 on_exit() { if [ "$CLUSTER_READY" -eq 0 ] && [ -n "$TP_CLUSTER_NAME" ]; then echo "Dumping TestPlatformCluster claim status for debugging:" oc get testplatformclusters.ci.openshift.org "$TP_CLUSTER_NAME" -o yaml fi } trap on_exit EXIT check_cluster_ready() { ec="$1" pj_url=$(jq -r '(.status.conditions // [])[]|select(.type == "_ProwJobURL").message' <<<"$ec") if [ ! -z "$pj_url" -a "$pj_url" != "null" -a $URL_DUMPED -eq 0 ]; then echo "ProwJob URL: $pj_url" URL_DUMPED=1 fi phase=$(jq -r '(.status.conditions // [])[]|select(.type == "_EphemeralClusterPhase").message' <<<"$ec") echo "Cluster phase: ${phase:-unknown}" if [ "$phase" = "Failed" ]; then echo "Error: failed to provision the cluster" exit 1 fi claim_ready=$(jq -r 'any((.status.conditions // [])[]; .type == "Ready" and .status == "True")' <<<"$ec") cluster_ready=$(jq -r 'any((.status.conditions // [])[]; .type == "ClusterReady" and .status == "True")' <<<"$ec") if [ "$claim_ready" = "true" ] && [ "$cluster_ready" = "true" ]; then CLUSTER_READY=1 echo "The cluster is ready" echo "SecretRef: $SECRET" echo -n "$SECRET" >"$(results.secretRef.path)" echo -n "$CLAIM_NAME" >"$(results.testPlatformClusterClaimName.path)" echo -n "$NAMESPACE" >"$(results.testPlatformClusterClaimNamespace.path)" exit 0 fi } watch_ephemeral_cluster() { local poll_interval=1m while true; do if ec=$(oc get testplatformclusters.ci.openshift.org "$TP_CLUSTER_NAME" -o json 2>&1); then check_cluster_ready "$ec" else echo "Warning: failed to get TestPlatformCluster '$TP_CLUSTER_NAME', retrying in ${poll_interval}..." echo "$ec" fi sleep "$poll_interval" done } # `start_timeout` is supposed to run as a subprocess. When the `sleep` is done, kill the parent, # that is this script. start_timeout() { sleep $CLUSTER_READY_TIMEOUT echo "Timed out: cluster has not come up in $CLUSTER_READY_TIMEOUT" kill $$ } cat <testplatformcluster_claim.yaml apiVersion: ci.openshift.org/v1alpha1 kind: TestPlatformCluster metadata: name: $CLAIM_NAME namespace: $NAMESPACE ownerReferences: - apiVersion: tekton.dev/v1 kind: $OWNER_KIND name: $OWNER_NAME uid: $OWNER_UID spec: ciOperator: buildRoot: $BUILD_ROOT baseImages: $BASE_IMAGES externalImages: $EXTERNAL_IMAGES releases: $RELEASES test: env: $ENV workflow: $WORKFLOW writeConnectionSecretToRef: name: $SECRET EOF # Add optional fields only when provided if [ -n "$RESOURCES" ]; then yq -i '.spec.ciOperator.resources = env(RESOURCES)' testplatformcluster_claim.yaml fi if [ -n "$CLUSTER_PROFILE" ]; then yq -i '.spec.ciOperator.test.clusterProfile = strenv(CLUSTER_PROFILE)' testplatformcluster_claim.yaml fi if [ "$CLUSTER_CLAIM" != '{}' ]; then yq -i '.spec.ciOperator.test.clusterClaim = env(CLUSTER_CLAIM)' testplatformcluster_claim.yaml fi echo "Generated TestPlatformCluster claim:" cat testplatformcluster_claim.yaml echo "Creating TestPlatformCluster claim..." TP_CLUSTER_NAME=$(oc create -f testplatformcluster_claim.yaml -o=jsonpath='{.metadata.name}') echo "Created TestPlatformCluster: $TP_CLUSTER_NAME" start_timeout & echo "Watching for cluster to become ready (timeout: $CLUSTER_READY_TIMEOUT)..." watch_ephemeral_cluster echo "Watch ended without detecting cluster ready or failed state" exit 1