name: Constellation create description: Create a new Constellation cluster using the latest OS image. inputs: workerNodesCount: description: "Number of worker nodes to spawn." required: true controlNodesCount: description: "Number of control-plane nodes to spawn." required: true cloudProvider: description: "Either 'gcp', 'aws' or 'azure'." required: true machineType: description: "Machine type of VM to spawn." required: false cliVersion: description: "Version of the CLI" required: true osImage: description: "OS image to use." required: true isDebugImage: description: "Is OS img a debug img?" required: true kubernetesVersion: description: "Kubernetes version to create the cluster from." required: false artifactNameSuffix: description: "Suffix for artifact naming." required: true fetchMeasurements: default: "false" description: "Update measurements via the 'constellation config fetch-measurements' command." azureSNPEnforcementPolicy: required: false description: "Azure SNP enforcement policy." test: description: "The e2e test payload." required: true azureClusterCreateCredentials: description: "Azure credentials authorized to create a Constellation cluster." required: true azureIAMCreateCredentials: description: "Azure credentials authorized to create an IAM configuration." required: true refStream: description: "Reference and stream of the image in use" required: false internalLoadBalancer: description: "Whether to use an internal load balancer for the control plane" required: false selfManagedInfra: description: "Use self-managed infrastructure instead of infrastructure created by the Constellation CLI." required: true marketplaceImageVersion: description: "Marketplace OS image version. Used instead of osImage." required: false force: description: "Set the force-flag on apply to ignore version mismatches." required: false outputs: kubeconfig: description: "The kubeconfig for the cluster." value: ${{ steps.constellation-init.outputs.KUBECONFIG }} osImageUsed: description: "The OS image used in the cluster." value: ${{ steps.setImage.outputs.image }} runs: using: "composite" steps: - name: Set constellation name shell: bash run: | yq eval -i "(.name) = \"e2e-test\"" constellation-conf.yaml - name: Set Azure SNP enforcement policy if: inputs.azureSNPEnforcementPolicy != '' shell: bash run: | if [[ ${{ inputs.cloudProvider }} != 'azure' ]]; then echo "SNP enforcement policy is only supported for Azure" exit 1 fi yq eval -i "(.attestation.azureSEVSNP.firmwareSignerConfig.enforcementPolicy) \ = \"${{ inputs.azureSNPEnforcementPolicy }}\"" constellation-conf.yaml - name: Set image id: setImage shell: bash env: imageInput: ${{ inputs.osImage }} run: | if [[ -z "${imageInput}" ]]; then echo "No image specified. Using default image from config." image=$(yq eval ".image" constellation-conf.yaml) echo "image=${image}" | tee -a "$GITHUB_OUTPUT" exit 0 fi yq eval -i "(.image) = \"${imageInput}\"" constellation-conf.yaml echo "image=${imageInput}" | tee -a "$GITHUB_OUTPUT" - name: Set marketplace image flag (Azure) if: inputs.marketplaceImageVersion != '' && inputs.cloudProvider == 'azure' shell: bash run: | yq eval -i "(.provider.azure.useMarketplaceImage) = true" constellation-conf.yaml yq eval -i "(.image) = \"${{ inputs.marketplaceImageVersion }}\"" constellation-conf.yaml - name: Update measurements for non-stable images if: inputs.fetchMeasurements shell: bash run: | constellation config fetch-measurements --debug --insecure - name: Set instanceType if: inputs.machineType && inputs.machineType != 'default' shell: bash run: | yq eval -i "(.nodeGroups[] | .instanceType) = \"${{ inputs.machineType }}\"" constellation-conf.yaml - name: Set node count shell: bash run: | yq eval -i "(.nodeGroups[] | select(.role == \"control-plane\") | .initialCount) = ${{ inputs.controlNodesCount }}" constellation-conf.yaml yq eval -i "(.nodeGroups[] | select(.role == \"worker\") | .initialCount) = ${{ inputs.workerNodesCount }}" constellation-conf.yaml - name: Enable debugCluster flag if: inputs.isDebugImage == 'true' shell: bash run: | yq eval -i '(.debugCluster) = true' constellation-conf.yaml - name: Enable internalLoadBalancer flag if: inputs.internalLoadBalancer == 'true' shell: bash run: | yq eval -i '(.internalLoadBalancer) = true' constellation-conf.yaml - name: Show Cluster Configuration shell: bash run: | echo "Creating cluster using config:" cat constellation-conf.yaml sudo sh -c 'echo "127.0.0.1 license.confidential.cloud" >> /etc/hosts' || true - name: Constellation create (CLI) if : inputs.selfManagedInfra != 'true' shell: bash run: | # TODO(v2.14): Remove workaround for CLIs not supporting apply command cmd='apply --skip-phases=init,attestationconfig,certsans,helm,image,k8s' if constellation --help | grep -q create; then cmd=create fi constellation $cmd -y --debug --tf-log=DEBUG - name: Constellation create (self-managed) if : inputs.selfManagedInfra == 'true' uses: ./.github/actions/self_managed_create with: cloudProvider: ${{ inputs.cloudProvider }} - name: Cdbg deploy if: inputs.isDebugImage == 'true' uses: ./.github/actions/cdbg_deploy with: cloudProvider: ${{ inputs.cloudProvider }} test: ${{ inputs.test }} azureClusterCreateCredentials: ${{ inputs.azureClusterCreateCredentials }} azureIAMCreateCredentials: ${{ inputs.azureIAMCreateCredentials }} refStream: ${{ inputs.refStream }} kubernetesVersion: ${{ inputs.kubernetesVersion }} selfManagedInfra: ${{ inputs.selfManagedInfra }} - name: Set force flag id: set-force-flag if: inputs.force == 'true' shell: bash run: | echo "flag=--force" | tee -a $GITHUB_OUTPUT - name: Constellation init id: constellation-init shell: bash run: | constellation apply --skip-phases=infrastructure --debug ${{ steps.set-force-flag.outputs.flag }} echo "KUBECONFIG=$(pwd)/constellation-admin.conf" | tee -a $GITHUB_OUTPUT - name: Wait for nodes to join and become ready shell: bash env: KUBECONFIG: "${{ steps.constellation-init.outputs.KUBECONFIG }}" JOINTIMEOUT: "1200" # 20 minutes timeout for all nodes to join run: | echo "::group::Wait for nodes" NODES_COUNT=$((${{ inputs.controlNodesCount }} + ${{ inputs.workerNodesCount }})) JOINWAIT=0 until [[ "$(kubectl get nodes -o json | jq '.items | length')" == "${NODES_COUNT}" ]] || [[ $JOINWAIT -gt $JOINTIMEOUT ]]; do echo "$(kubectl get nodes -o json | jq '.items | length')/"${NODES_COUNT}" nodes have joined.. waiting.." JOINWAIT=$((JOINWAIT+30)) sleep 30 done if [[ $JOINWAIT -gt $JOINTIMEOUT ]]; then echo "Timed out waiting for nodes to join" exit 1 fi echo "$(kubectl get nodes -o json | jq '.items | length')/"${NODES_COUNT}" nodes have joined" if ! kubectl wait --for=condition=ready --all nodes --timeout=20m; then kubectl get pods -n kube-system kubectl get events -n kube-system echo "::error::kubectl wait timed out before all nodes became ready" echo "::endgroup::" exit 1 fi echo "::endgroup::" - name: Download boot logs if: always() continue-on-error: true shell: bash env: CSP: ${{ inputs.cloudProvider }} run: | echo "::group::Download boot logs" CONSTELL_UID=$(yq '.infrastructure.uid' constellation-state.yaml) case $CSP in azure) AZURE_RESOURCE_GROUP=$(yq eval ".provider.azure.resourceGroup" constellation-conf.yaml) ./.github/actions/constellation_create/az-logs.sh ${AZURE_RESOURCE_GROUP} ;; gcp) GCP_ZONE=$(yq eval ".provider.gcp.zone" constellation-conf.yaml) ./.github/actions/constellation_create/gcp-logs.sh ${GCP_ZONE} ${CONSTELL_UID} ;; aws) ./.github/actions/constellation_create/aws-logs.sh us-east-2 ${CONSTELL_UID} ;; esac echo "::endgroup::" - name: Upload boot logs if: always() && !env.ACT continue-on-error: true uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: serial-logs-${{ inputs.artifactNameSuffix }} path: | *.log !terraform.log