From 4ddb6a18163d26c5807af9aeb6c77dcafb0fc4a0 Mon Sep 17 00:00:00 2001 From: Giovanni Ferri Date: Tue, 24 Mar 2026 02:24:52 +0000 Subject: [PATCH 1/5] feat(oci): add deploy workflow + omni-ready mode + credential-triggered replace - Add .github/workflows/deploy.yml: plan on PR, apply on push/dispatch - Add terraform.tfvars: omni_ready=true, omni_endpoint=omni.wind-bearded.ts.net:8090 - Add terraform_data.omni_credentials: replaces Ampere instances when omni_join_token or tailscale_auth_key changes (sha256 hash triggers) - Remove prevent_destroy and metadata from ignore_changes on Ampere instances so replacements can propagate new user_data to nodes Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/deploy.yml | 143 +++++++++++++++++++++++++++++++++++ tofu/oci/main.tf | 19 +++-- tofu/oci/terraform.tfvars | 9 +++ tofu/oci/validation.tf | 10 +-- 4 files changed, 169 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/deploy.yml create mode 100644 tofu/oci/terraform.tfvars diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..40d50d0 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,143 @@ +name: Deploy + +on: + push: + branches: ["main"] + paths: + - "tofu/oci/**" + pull_request: + branches: ["main"] + paths: + - "tofu/oci/**" + workflow_dispatch: + inputs: + reason: + description: "Reason for manual deploy" + required: false + default: "manual" + +permissions: + contents: read + pull-requests: write + +jobs: + plan: + name: Tofu Plan + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Write OCI credentials + run: | + mkdir -p ~/.oci + printf '%s' "$OCI_API_KEY_PEM" > ~/.oci/oci_api_key.pem + chmod 600 ~/.oci/oci_api_key.pem + cat > ~/.oci/config < ~/.oci/oci_api_key.pem + chmod 600 ~/.oci/oci_api_key.pem + cat > ~/.oci/config < Date: Tue, 24 Mar 2026 02:28:33 +0000 Subject: [PATCH 2/5] fix(ci): write backend-config from secret + fix tofu fmt Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/deploy.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 40d50d0..6c7ea3c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -49,6 +49,11 @@ jobs: OCI_TENANCY_OCID: ${{ secrets.OCI_TENANCY_OCID }} OCI_API_KEY_PEM: ${{ secrets.OCI_API_KEY_PEM }} + - name: Write backend config + run: printf '%s' "$TF_BACKEND_CONFIG" > tofu/oci/backend-config.tfvars + env: + TF_BACKEND_CONFIG: ${{ secrets.TF_BACKEND_CONFIG }} + - name: Setup OpenTofu uses: opentofu/setup-opentofu@v2.0.0 with: @@ -115,6 +120,11 @@ jobs: OCI_TENANCY_OCID: ${{ secrets.OCI_TENANCY_OCID }} OCI_API_KEY_PEM: ${{ secrets.OCI_API_KEY_PEM }} + - name: Write backend config + run: printf '%s' "$TF_BACKEND_CONFIG" > tofu/oci/backend-config.tfvars + env: + TF_BACKEND_CONFIG: ${{ secrets.TF_BACKEND_CONFIG }} + - name: Setup OpenTofu uses: opentofu/setup-opentofu@v2.0.0 with: From 77aaae7e4fb9572f0bf1b39537a71c6a2e2c1d35 Mon Sep 17 00:00:00 2001 From: Giovanni Ferri Date: Tue, 24 Mar 2026 02:36:33 +0000 Subject: [PATCH 3/5] =?UTF-8?q?style(tofu):=20fix=20fmt=20=E2=80=94=20remo?= =?UTF-8?q?ve=20spaces=20after=20!=20operator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- tofu/oci/main.tf | 2 +- tofu/oci/validation.tf | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tofu/oci/main.tf b/tofu/oci/main.tf index 5c3da87..912140e 100644 --- a/tofu/oci/main.tf +++ b/tofu/oci/main.tf @@ -260,7 +260,7 @@ resource "oci_core_instance" "ampere_instance" { # user_data: Talos MachineConfig for omni_ready mode (null = omit for Ubuntu) var.omni_ready ? { user_data = base64encode(local._ampere_user_data) } : {}, # ssh_authorized_keys: Ubuntu cloud-init only (Talos ignores this) - ! var.omni_ready && var.ssh_public_key != null ? { ssh_authorized_keys = var.ssh_public_key } : {}, + !var.omni_ready && var.ssh_public_key != null ? { ssh_authorized_keys = var.ssh_public_key } : {}, ) lifecycle { diff --git a/tofu/oci/validation.tf b/tofu/oci/validation.tf index 62f6f2a..00fba59 100644 --- a/tofu/oci/validation.tf +++ b/tofu/oci/validation.tf @@ -79,28 +79,28 @@ check "micro_min_boot_vol" { check "omni_ready_requires_talos_image" { assert { - condition = ! var.omni_ready || var.talos_image_ocid != null + condition = !var.omni_ready || var.talos_image_ocid != null error_message = "omni_ready = true requires talos_image_ocid. Import the Talos+Tailscale Image Factory image and set talos_image_ocid." } } check "omni_ready_requires_endpoint" { assert { - condition = ! var.omni_ready || var.omni_endpoint != null + condition = !var.omni_ready || var.omni_endpoint != null error_message = "omni_ready = true requires omni_endpoint (e.g. omni.wind-bearded.ts.net:8090)." } } check "omni_ready_requires_join_token" { assert { - condition = ! var.omni_ready || var.omni_join_token != null + condition = !var.omni_ready || var.omni_join_token != null error_message = "omni_ready = true requires omni_join_token. Get from: omnictl get connections -o yaml | grep joinToken." } } check "omni_ready_requires_tailscale_key" { assert { - condition = ! var.omni_ready || var.tailscale_auth_key != null + condition = !var.omni_ready || var.tailscale_auth_key != null error_message = "omni_ready = true requires tailscale_auth_key with tag:oci applied." } } @@ -111,7 +111,7 @@ check "omni_ready_requires_tailscale_key" { check "compartment_name_required" { assert { - condition = ! var.create_compartment || var.compartment_name != null + condition = !var.create_compartment || var.compartment_name != null error_message = "compartment_name is required when create_compartment = true." } } From 240c093be95f3ed4f9754eafdda2c04d15504c2b Mon Sep 17 00:00:00 2001 From: Giovanni Ferri Date: Tue, 24 Mar 2026 03:07:54 +0000 Subject: [PATCH 4/5] fix(ci): fetch talos image OCID from oci-talos-gitops-apps instead of manual variable Replace the static `vars.TALOS_IMAGE_OCID` GitHub variable with a dynamic fetch from the `oci-talos-gitops-apps/omni/talos-image.yaml` file, which is auto-updated by the talos-images build workflow. This removes the need to manually update a GitHub variable on each Talos upgrade. The deploy workflow now always uses the latest image that was successfully imported into OCI. Also simplify the apply job: download the pre-built plan artifact and apply it directly (`tofu apply tfplan`) rather than re-running plan with all the same vars. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/deploy.yml | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6c7ea3c..fd2c7db 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -29,6 +29,20 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Fetch Talos image OCID from oci-talos-gitops-apps + id: talos_image + run: | + OCID=$(curl -fsSL \ + "https://raw.githubusercontent.com/syscode-labs/oci-talos-gitops-apps/main/omni/talos-image.yaml" \ + | grep '^oci_image_ocid:' | awk '{print $2}') + if [ -z "$OCID" ] || [ "$OCID" = '""' ] || [ "$OCID" = "null" ]; then + echo "ERROR: oci_image_ocid is empty in oci-talos-gitops-apps/omni/talos-image.yaml" + echo "Run the talos-images build workflow first to import a Talos image into OCI." + exit 1 + fi + echo "ocid=${OCID}" >> "$GITHUB_OUTPUT" + echo "Talos image OCID: ${OCID}" + - name: Write OCI credentials run: | mkdir -p ~/.oci @@ -69,7 +83,7 @@ jobs: tofu plan \ -var="tenancy_ocid=$OCI_TENANCY_OCID" \ -var="compartment_ocid=$OCI_COMPARTMENT_OCID" \ - -var="talos_image_ocid=$TALOS_IMAGE_OCID" \ + -var="talos_image_ocid=${{ steps.talos_image.outputs.ocid }}" \ -var="omni_join_token=$OMNI_JOIN_TOKEN" \ -var="tailscale_auth_key=$TAILSCALE_AUTH_KEY" \ -var-file=terraform.tfvars \ @@ -77,7 +91,6 @@ jobs: env: OCI_TENANCY_OCID: ${{ secrets.OCI_TENANCY_OCID }} OCI_COMPARTMENT_OCID: ${{ secrets.OCI_COMPARTMENT_OCID }} - TALOS_IMAGE_OCID: ${{ vars.TALOS_IMAGE_OCID }} OMNI_JOIN_TOKEN: ${{ secrets.OMNI_JOIN_TOKEN }} TAILSCALE_AUTH_KEY: ${{ secrets.TAILSCALE_AUTH_KEY }} TF_LOG: WARN @@ -134,20 +147,15 @@ jobs: working-directory: tofu/oci run: tofu init -backend-config=backend-config.tfvars + - name: Download plan artifact + uses: actions/download-artifact@v4 + with: + name: tfplan + path: tofu/oci + - name: Tofu Apply working-directory: tofu/oci - run: | - tofu apply -auto-approve \ - -var="tenancy_ocid=$OCI_TENANCY_OCID" \ - -var="compartment_ocid=$OCI_COMPARTMENT_OCID" \ - -var="talos_image_ocid=$TALOS_IMAGE_OCID" \ - -var="omni_join_token=$OMNI_JOIN_TOKEN" \ - -var="tailscale_auth_key=$TAILSCALE_AUTH_KEY" \ - -var-file=terraform.tfvars + run: tofu apply -auto-approve tfplan env: OCI_TENANCY_OCID: ${{ secrets.OCI_TENANCY_OCID }} - OCI_COMPARTMENT_OCID: ${{ secrets.OCI_COMPARTMENT_OCID }} - TALOS_IMAGE_OCID: ${{ vars.TALOS_IMAGE_OCID }} - OMNI_JOIN_TOKEN: ${{ secrets.OMNI_JOIN_TOKEN }} - TAILSCALE_AUTH_KEY: ${{ secrets.TAILSCALE_AUTH_KEY }} TF_LOG: WARN From feed3694b313b575bd06688032ec593e78497107 Mon Sep 17 00:00:00 2001 From: Giovanni Ferri Date: Tue, 24 Mar 2026 03:09:21 +0000 Subject: [PATCH 5/5] =?UTF-8?q?fix(ci):=20skip=20state=20locking=20in=20pl?= =?UTF-8?q?an/apply=20=E2=80=94=20PAR=20URL=20backend=20returns=20404=20on?= =?UTF-8?q?=20lock=20DELETE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OCI PAR URL HTTP backend returns 404 when tofu tries to DELETE the lock file after plan completes, causing the CI step to fail even though the plan itself succeeded. Add -lock=false to both plan and apply commands. Locking is not meaningful in CI: plan and apply run serially in separate jobs, and the plan artifact is the single source of truth between them. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/deploy.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index fd2c7db..9a8c118 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -81,6 +81,7 @@ jobs: working-directory: tofu/oci run: | tofu plan \ + -lock=false \ -var="tenancy_ocid=$OCI_TENANCY_OCID" \ -var="compartment_ocid=$OCI_COMPARTMENT_OCID" \ -var="talos_image_ocid=${{ steps.talos_image.outputs.ocid }}" \ @@ -155,7 +156,7 @@ jobs: - name: Tofu Apply working-directory: tofu/oci - run: tofu apply -auto-approve tfplan + run: tofu apply -lock=false -auto-approve tfplan env: OCI_TENANCY_OCID: ${{ secrets.OCI_TENANCY_OCID }} TF_LOG: WARN