-
Notifications
You must be signed in to change notification settings - Fork 0
ci(podvm): build tdx-snp-nvidia image variant #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: cohere
Are you sure you want to change the base?
Changes from all commits
edc4a45
84e6078
61b5476
93c4764
59293f2
0def16d
f648055
6ae57be
1a14a25
ed24de7
6f4974a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,254 @@ | ||
| name: Deploy PodVM to Azure (Cohere) | ||
|
|
||
| # Publishes a podvm artifact to an Azure Compute Gallery image version flagged | ||
| # ConfidentialVmSupported (x86_64/Gen2) for SEV-SNP VMs (e.g. NCCadsH100v5). | ||
| # Requires (infra-azure): | ||
| # secrets: AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_SUBSCRIPTION_ID (OIDC app | ||
| # with Contributor + Storage Blob Data Contributor) | ||
| # vars: AZURE_RESOURCE_GROUP, AZURE_PODVM_GALLERY_NAME, | ||
| # AZURE_PODVM_STORAGE_ACCOUNT, AZURE_PODVM_BLOB_CONTAINER, | ||
| # AZURE_LOCATION | ||
|
|
||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| image_tag: | ||
| description: "OCI artifact tag in GHCR to deploy" | ||
| required: true | ||
| type: string | ||
| secrets: | ||
| AZURE_CLIENT_ID: | ||
| required: true | ||
| AZURE_TENANT_ID: | ||
| required: true | ||
| AZURE_SUBSCRIPTION_ID: | ||
| required: true | ||
|
|
||
| workflow_dispatch: | ||
| inputs: | ||
| image_tag: | ||
| description: "OCI artifact tag in GHCR to deploy" | ||
| required: true | ||
| type: string | ||
|
|
||
| permissions: {} | ||
|
|
||
| env: | ||
| OCI_IMAGE: ghcr.io/${{ github.repository }}/podvm | ||
| VERSIONS_YAML: src/cloud-api-adaptor/versions.yaml | ||
|
|
||
| jobs: | ||
| deploy: | ||
| name: Azure | ||
| runs-on: ubuntu-latest | ||
| # Serialize publishes per image tag: gallery versions are immutable and the | ||
| # patch is time-based, so overlapping runs could collide on the same version. | ||
| concurrency: | ||
| group: deploy-azure-cohere-${{ inputs.image_tag }} | ||
| cancel-in-progress: false | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gallery version collision across tagsMedium Severity Azure gallery versions use minute-granularity Additional Locations (1)Reviewed by Cursor Bugbot for commit 6f4974a. Configure here. |
||
| permissions: | ||
| id-token: write # Azure login via OIDC federated credentials | ||
| contents: read # actions/checkout | ||
| packages: read # pull OCI artifact from GHCR | ||
| attestations: read # gh attestation verify | ||
| steps: | ||
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt-get update -qq | ||
| sudo apt-get install -y --no-install-recommends qemu-utils | ||
| command -v yq || sudo snap install yq | ||
| ORAS_VERSION=$(yq '.tools.oras' "$VERSIONS_YAML") | ||
| curl -fsSLO "https://github.com/oras-project/oras/releases/download/v${ORAS_VERSION}/oras_${ORAS_VERSION}_linux_amd64.tar.gz" | ||
| tar -xzf "oras_${ORAS_VERSION}_linux_amd64.tar.gz" oras | ||
| sudo mv oras /usr/local/bin/ | ||
| oras version | ||
| qemu-img --version | ||
|
|
||
| - name: Login to GHCR | ||
| uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Verify attestation | ||
| id: verify | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| IMAGE_TAG: ${{ inputs.image_tag }} | ||
| run: | | ||
| IMAGE_DIGEST=$(oras resolve "${OCI_IMAGE}:${IMAGE_TAG}") | ||
| IMAGE_REF="${OCI_IMAGE}@${IMAGE_DIGEST}" | ||
| gh attestation verify \ | ||
| "oci://${IMAGE_REF}" \ | ||
| --repo "${GITHUB_REPOSITORY}" | ||
| echo "image_ref=$IMAGE_REF" >> "$GITHUB_OUTPUT" | ||
| echo "Attestation verified for ${IMAGE_REF}" | ||
|
|
||
| - name: Pull artifact and read measurements | ||
| id: pull | ||
| env: | ||
| IMAGE_REF: ${{ steps.verify.outputs.image_ref }} | ||
| run: | | ||
| mkdir -p /tmp/podvm | ||
| cd /tmp/podvm | ||
| oras pull "${IMAGE_REF}" | ||
| ls -lh | ||
|
|
||
| if [ ! -f measurements.json ]; then | ||
| echo "ERROR: measurements.json not found in artifact - build is broken" | ||
| exit 1 | ||
| fi | ||
| cat measurements.json | ||
|
|
||
| IMAGE_NAME=$(jq -r '.image_name' measurements.json) | ||
| IMAGE_FAMILY=$(jq -r '.image_family // ""' measurements.json) | ||
| if [ -z "$IMAGE_NAME" ] || [ "$IMAGE_NAME" = "null" ] || [ -z "$IMAGE_FAMILY" ]; then | ||
| echo "ERROR: image_name/image_family missing in measurements.json" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Definition names: [A-Za-z0-9._-], <=80 chars. | ||
| IMAGE_DEF=$(echo "$IMAGE_FAMILY" | sed 's/[^A-Za-z0-9._-]/-/g' | cut -c1-80) | ||
|
|
||
| # Versions need numeric Major.Minor.Patch, no leading zeros. | ||
| MAJOR=$(date -u +%Y) | ||
| MINOR=$(date -u +%m%d | sed 's/^0*//'); MINOR=${MINOR:-0} | ||
| PATCH=$(date -u +%H%M | sed 's/^0*//'); PATCH=${PATCH:-0} | ||
| IMAGE_VERSION="${MAJOR}.${MINOR}.${PATCH}" | ||
|
|
||
| { | ||
| echo "image_name=$IMAGE_NAME" | ||
| echo "image_def=$IMAGE_DEF" | ||
| echo "image_version=$IMAGE_VERSION" | ||
| echo "tee_platform=$(jq -r '.tee_platform // "tdx"' measurements.json)" | ||
| echo "distro=$(jq -r '.distro' measurements.json)" | ||
| echo "profile=$(jq -r '.profile' measurements.json)" | ||
| echo "caa_commit=$(jq -r '.caa_commit' measurements.json)" | ||
| } >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Extract raw disk and convert to fixed VHD | ||
| id: vhd | ||
| run: | | ||
| cd /tmp/podvm | ||
| tar -xzf disk.tar.gz | ||
| # Azure needs a fixed-format VHD whose virtual size is a whole number | ||
| # of MiB. mkosi's raw image isn't necessarily 1 MiB-aligned, so round | ||
| # it up to the next whole MiB first; qemu-img's force_size then keeps | ||
| # that exact aligned size in the fixed VHD footer. Without this, the | ||
| # gallery image-version create fails with "unsupported virtual size". | ||
| MB=$((1024 * 1024)) | ||
| size=$(qemu-img info -f raw --output json disk.raw | jq '.["virtual-size"]') | ||
| rounded=$(( ((size + MB - 1) / MB) * MB )) | ||
| if [ "$rounded" -ne "$size" ]; then | ||
| echo "Rounding raw image $size -> $rounded bytes ($((rounded / MB)) MiB) for Azure alignment" | ||
| qemu-img resize -f raw disk.raw "$rounded" | ||
| fi | ||
| qemu-img convert -f raw -O vpc -o subformat=fixed,force_size \ | ||
| disk.raw disk.vhd | ||
| ls -lh disk.vhd | ||
|
|
||
| - name: Azure login (OIDC) | ||
| uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0 | ||
| with: | ||
| client-id: ${{ secrets.AZURE_CLIENT_ID }} | ||
| tenant-id: ${{ secrets.AZURE_TENANT_ID }} | ||
| subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | ||
|
|
||
| - name: Publish confidential gallery image version | ||
| env: | ||
| RESOURCE_GROUP: ${{ vars.AZURE_RESOURCE_GROUP }} | ||
| GALLERY: ${{ vars.AZURE_PODVM_GALLERY_NAME }} | ||
| STORAGE_ACCOUNT: ${{ vars.AZURE_PODVM_STORAGE_ACCOUNT }} | ||
| BLOB_CONTAINER: ${{ vars.AZURE_PODVM_BLOB_CONTAINER }} | ||
| LOCATION: ${{ vars.AZURE_LOCATION }} | ||
| IMAGE_NAME: ${{ steps.pull.outputs.image_name }} | ||
| IMAGE_DEF: ${{ steps.pull.outputs.image_def }} | ||
| IMAGE_VERSION: ${{ steps.pull.outputs.image_version }} | ||
| TEE_PLATFORM: ${{ steps.pull.outputs.tee_platform }} | ||
| CAA_COMMIT: ${{ steps.pull.outputs.caa_commit }} | ||
| run: | | ||
| set -euo pipefail | ||
| for v in RESOURCE_GROUP GALLERY STORAGE_ACCOUNT BLOB_CONTAINER LOCATION; do | ||
| if [ -z "${!v}" ]; then | ||
| echo "ERROR: required Azure var '$v' is empty - configure the infra-azure prerequisites" >&2 | ||
| exit 1 | ||
| fi | ||
| done | ||
|
|
||
| BLOB_NAME="${IMAGE_NAME}.vhd" | ||
| BLOB_URI="https://${STORAGE_ACCOUNT}.blob.core.windows.net/${BLOB_CONTAINER}/${BLOB_NAME}" | ||
|
|
||
| echo "Uploading staging VHD page blob: $BLOB_URI" | ||
| az storage blob upload \ | ||
| --account-name "$STORAGE_ACCOUNT" \ | ||
| --container-name "$BLOB_CONTAINER" \ | ||
| --name "$BLOB_NAME" \ | ||
| --file /tmp/podvm/disk.vhd \ | ||
| --type page \ | ||
| --auth-mode login \ | ||
| --overwrite true | ||
|
|
||
| # Create the ConfidentialVmSupported definition on first use. | ||
| if ! az sig image-definition show \ | ||
| --resource-group "$RESOURCE_GROUP" \ | ||
| --gallery-name "$GALLERY" \ | ||
| --gallery-image-definition "$IMAGE_DEF" >/dev/null 2>&1; then | ||
| echo "Creating image definition $IMAGE_DEF (ConfidentialVmSupported)" | ||
| az sig image-definition create \ | ||
| --resource-group "$RESOURCE_GROUP" \ | ||
| --gallery-name "$GALLERY" \ | ||
| --gallery-image-definition "$IMAGE_DEF" \ | ||
| --publisher cohere \ | ||
| --offer podvm \ | ||
| --sku "$IMAGE_DEF" \ | ||
| --os-type Linux \ | ||
| --os-state Generalized \ | ||
| --hyper-v-generation V2 \ | ||
| --architecture x64 \ | ||
| --features SecurityType=ConfidentialVmSupported | ||
| fi | ||
|
|
||
| if az sig image-version show \ | ||
| --resource-group "$RESOURCE_GROUP" \ | ||
| --gallery-name "$GALLERY" \ | ||
| --gallery-image-definition "$IMAGE_DEF" \ | ||
| --gallery-image-version "$IMAGE_VERSION" >/dev/null 2>&1; then | ||
| echo "ERROR: image version $IMAGE_DEF:$IMAGE_VERSION already exists" >&2 | ||
| echo "Gallery versions are immutable; re-run to bump the time-based patch." >&2 | ||
| exit 1 | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| fi | ||
|
|
||
| echo "Creating gallery image version $IMAGE_DEF:$IMAGE_VERSION" | ||
| az sig image-version create \ | ||
| --resource-group "$RESOURCE_GROUP" \ | ||
| --gallery-name "$GALLERY" \ | ||
| --gallery-image-definition "$IMAGE_DEF" \ | ||
| --gallery-image-version "$IMAGE_VERSION" \ | ||
| --location "$LOCATION" \ | ||
| --os-vhd-storage-account "$STORAGE_ACCOUNT" \ | ||
| --os-vhd-uri "$BLOB_URI" \ | ||
| --tags "tee=${TEE_PLATFORM}" "image_name=${IMAGE_NAME}" "caa_commit=${CAA_COMMIT}" | ||
|
|
||
| echo "Cleaning up staging VHD blob" | ||
| az storage blob delete \ | ||
| --account-name "$STORAGE_ACCOUNT" \ | ||
| --container-name "$BLOB_CONTAINER" \ | ||
| --name "$BLOB_NAME" \ | ||
| --auth-mode login || true | ||
|
|
||
| { | ||
| echo "### PodVM Azure gallery image published" | ||
| echo "" | ||
| echo "| Field | Value |" | ||
| echo "| --- | --- |" | ||
| echo "| Gallery | \`${GALLERY}\` |" | ||
| echo "| Definition | \`${IMAGE_DEF}\` |" | ||
| echo "| Version | \`${IMAGE_VERSION}\` |" | ||
| echo "| Security type | ConfidentialVmSupported |" | ||
| echo "| TEE | ${TEE_PLATFORM} |" | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,7 @@ on: | |
| workflow_call: | ||
| inputs: | ||
| image_tag: | ||
| description: "OCI artifact tag in GHCR (e.g. podvm-ubuntu-tdx-nvidia-release-v1-0-0)" | ||
| description: "OCI artifact tag in GHCR (e.g. podvm-ubuntu-tdx-snp-nvidia-release-v1-0-0)" | ||
| required: true | ||
| type: string | ||
| secrets: | ||
|
|
@@ -27,7 +27,6 @@ permissions: | |
| attestations: read # gh attestation verify | ||
|
|
||
| env: | ||
| TEE_PLATFORM: tdx | ||
| OCI_IMAGE: ghcr.io/${{ github.repository }}/podvm | ||
| PODVM_GCP_IMAGE_PROJECT: cohere-artifacts | ||
| VERSIONS_YAML: src/cloud-api-adaptor/versions.yaml | ||
|
|
@@ -118,6 +117,7 @@ jobs: | |
| echo "caa_commit_short=$(jq -r '.caa_commit[:7]' measurements.json)" | ||
| echo "caa_version=$(jq -r '.caa_version' measurements.json)" | ||
| echo "caa_ref=$(jq -r '.caa_ref' measurements.json)" | ||
| echo "tee_platform=$(jq -r '.tee_platform' measurements.json)" | ||
| echo "rtmr2=$(jq -r '.rtmr2_sha384 // ""' measurements.json)" | ||
| echo "kernel=$(jq -r '.kernel // ""' measurements.json)" | ||
| echo "build_date=$(jq -r '.build_date // ""' measurements.json)" | ||
|
|
@@ -156,6 +156,7 @@ jobs: | |
| CAA_COMMIT_SHORT: ${{ steps.pull.outputs.caa_commit_short }} | ||
| CAA_REF: ${{ steps.pull.outputs.caa_ref }} | ||
| PROFILE: ${{ steps.pull.outputs.profile }} | ||
| TEE_PLATFORM: ${{ steps.pull.outputs.tee_platform }} | ||
| RTMR2: ${{ steps.pull.outputs.rtmr2 }} | ||
| KERNEL: ${{ steps.pull.outputs.kernel }} | ||
| BUILD_DATE: ${{ steps.pull.outputs.build_date }} | ||
|
|
@@ -174,6 +175,30 @@ jobs: | |
| exit 1 | ||
| fi | ||
|
|
||
| if [ -z "$TEE_PLATFORM" ] || [ "$TEE_PLATFORM" = "null" ]; then | ||
| echo "ERROR: tee_platform is missing from measurements.json" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| case "$TEE_PLATFORM" in | ||
| tdx-snp-nvidia) | ||
| GUEST_OS_FEATURES="TDX_CAPABLE,SEV_CAPABLE,SEV_SNP_CAPABLE,UEFI_COMPATIBLE,GVNIC" | ||
| ;; | ||
| tdx) | ||
| GUEST_OS_FEATURES="TDX_CAPABLE,UEFI_COMPATIBLE,GVNIC" | ||
| ;; | ||
| snp|amd) | ||
| GUEST_OS_FEATURES="SEV_SNP_CAPABLE,UEFI_COMPATIBLE,GVNIC" | ||
| ;; | ||
| sev) | ||
| GUEST_OS_FEATURES="SEV_CAPABLE,UEFI_COMPATIBLE,GVNIC" | ||
| ;; | ||
| *) | ||
| echo "ERROR: unsupported tee_platform '$TEE_PLATFORM'" >&2 | ||
| exit 1 | ||
| ;; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing legacy tee_platform mappingLow Severity GCP guest OS feature selection rejects any Reviewed by Cursor Bugbot for commit edc4a45. Configure here. |
||
| esac | ||
|
|
||
| RAW_IMAGE="${IMAGE_NAME}-import" | ||
| if gcloud compute images describe "$RAW_IMAGE" --project="$GCP_PROJECT" &>/dev/null; then | ||
| echo "Deleting stale intermediate image $RAW_IMAGE" | ||
|
|
@@ -204,7 +229,7 @@ jobs: | |
| --family="$IMAGE_FAMILY" \ | ||
| --description="podvm: distro=${DISTRO} tee=${TEE_PLATFORM} profile=${PROFILE} class=${IMAGE_CLASS} caa=${CAA_VERSION} commit=${CAA_COMMIT} rtmr2=${RTMR2} oci=${OCI_IMAGE}:${IMAGE_TAG}" \ | ||
| --labels="$LABELS" \ | ||
| --guest-os-features=TDX_CAPABLE,UEFI_COMPATIBLE,GVNIC | ||
| --guest-os-features="$GUEST_OS_FEATURES" | ||
|
|
||
| gcloud compute images delete "$RAW_IMAGE" \ | ||
| --project="$GCP_PROJECT" --quiet | ||
|
|
||


Uh oh!
There was an error while loading. Please reload this page.