Skip to content

ci: edge-cluster-trigger workflow (GH→ADO smoke test)#52

Open
entlein wants to merge 1 commit into
mainfrom
wire/ado-edge-trigger
Open

ci: edge-cluster-trigger workflow (GH→ADO smoke test)#52
entlein wants to merge 1 commit into
mainfrom
wire/ado-edge-trigger

Conversation

@entlein

@entlein entlein commented May 29, 2026

Copy link
Copy Markdown

Summary

Adds .github/workflows/edge-cluster-trigger.yml — a workflow_dispatch-only
workflow that exercises the GitHub→Azure DevOps trigger path for the
SovereignSOC edge-cluster pipeline. Uses the shared composite action at
k8sstormcenter/actions/trigger-ado-pipeline@main.

Once verified, the same call can be folded into component-tests.yaml as a
pre-step that resets the target edge slot before the 33-test component
suite runs against it (current setup uses ephemeral Kind).

Required GH config (already in place)

Where Key Value
org secret ADO_PAT PAT with Build:Read & Execute
org var ADO_ORG AustrianDataLab
org var ADO_PROJECT SovereignSOC
repo var ADO_PIPELINE_EDGE 61

The workflow guards on these and prints exact missing keys if any are unset.

Test plan

  • Trigger workflow with action=plan, cluster=edge4 (safe — no infra mutation)
  • Verify the ADO run completes successfully and the run URL appears in the step summary
  • (Optional) Trigger with action=reset against edge4 for a real lifecycle test

🤖 Generated with Claude Code

Smoke-test workflow that fires SovereignSOC's edge-cluster ADO pipeline
via the shared composite action at k8sstormcenter/actions/
trigger-ado-pipeline. workflow_dispatch only for now — once the chain
is verified, we can fold it into component-tests.yaml as a pre-step
that resets the target edge slot before the 33-test component suite
runs against it.

Required GH config (one-off):
  secrets.ADO_PAT       (org-level, scope Build:Read & Execute)
  vars.ADO_ORG          = AustrianDataLab
  vars.ADO_PROJECT      = SovereignSOC
  vars.ADO_PIPELINE_EDGE = 61

A guard step fails fast if any of those are missing, with the exact
key names to set.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This pull request introduces a new GitHub Actions workflow file (.github/workflows/edge-cluster-trigger.yml) that enables manual triggering of ADO edge-cluster lifecycle operations. The workflow accepts cluster slot, lifecycle action, base OS, and wait-flag inputs, validates required ADO configuration upfront, calls the ADO trigger action, and reports the ADO run details and result in the step summary.

Changes

Edge Cluster Lifecycle Manual Trigger Workflow

Layer / File(s) Summary
Workflow dispatch inputs and metadata
.github/workflows/edge-cluster-trigger.yml
workflow_dispatch entry point defines inputs for cluster slot, lifecycle action (plan/create/delete/reset), base OS, and wait-for-completion flag; each input has default values and choice constraints.
Configuration validation guard
.github/workflows/edge-cluster-trigger.yml
Early step exports ADO secrets and variables to environment, validates non-empty required fields (connection string, PAT, organization, project, pipeline ID), annotates errors in GitHub UI, and fails the workflow if any required value is missing.
ADO pipeline trigger and summary reporting
.github/workflows/edge-cluster-trigger.yml
Calls k8sstormcenter/actions/trigger-ado-pipeline@main with mapped inputs and 1800-second timeout; unconditional final step writes the selected cluster, action, ADO run ID, run URL, and result to the GitHub step summary.

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: a new GitHub Actions workflow for triggering edge-cluster operations via Azure DevOps, positioned as a smoke test.
Description check ✅ Passed The description is clearly related to the changeset, explaining the purpose of the new workflow, required configuration, and test plan.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch wire/ado-edge-trigger

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/edge-cluster-trigger.yml:
- Around line 13-48: Add a concurrency group to the workflow to prevent
simultaneous lifecycle actions on the same cluster by using the cluster input as
the key; specifically insert a concurrency block after the on: section (before
jobs:) that sets group to something like "edge-cluster-${{ github.workflow
}}-${{ github.event.inputs.cluster }}" (or similar using inputs.cluster) and
optionally set cancel-in-progress: true to let newer runs cancel older ones;
this will serialize runs for the same cluster while still allowing parallel runs
for different clusters and references the existing inputs.cluster and the
workflow's trigger.
- Around line 86-95: The Summary step currently injects step outputs directly
into a shell script which can allow shell metacharacters to be interpreted;
instead, capture the ADO outputs into environment variables (e.g., ADO_RUN_ID,
ADO_RUN_URL, ADO_RESULT from steps.ado.outputs.run_id/run_url/result) and then
append them to GITHUB_STEP_SUMMARY using safe, quoted expansion or printf (e.g.,
use printf '%s\n' "$ADO_RUN_ID") so that backticks/$() are not executed; modify
the "Summary" run block to export/use those env vars and ensure all variable
expansions are quoted when writing to GITHUB_STEP_SUMMARY.
- Around line 73-84: Update the GitHub Action step that defines the "ado" job to
pin the external action instead of using the floating ref: replace the uses
string "k8sstormcenter/actions/trigger-ado-pipeline@main" (in the step with id:
ado) with the commit SHA
"k8sstormcenter/actions/trigger-ado-pipeline@27790fc0392ded46e74bf2d7620c0b790cde2681";
keep the other inputs (org, project, pipeline_id, pat, cluster, action, os,
wait, timeout) unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b145d083-a039-4706-8edf-e205e2bacb02

📥 Commits

Reviewing files that changed from the base of the PR and between 58247d7 and 25a8ff5.

📒 Files selected for processing (1)
  • .github/workflows/edge-cluster-trigger.yml

Comment on lines +13 to +48
on:
workflow_dispatch:
inputs:
cluster:
description: 'Edge cluster slot'
required: true
default: 'edge4'
type: choice
options:
- edge4
- k3s-1
- k3s-2
- rocky-1
action:
description: 'Lifecycle action'
required: true
default: 'plan'
type: choice
options:
- plan
- create
- delete
- reset
os:
description: 'Base OS'
required: true
default: 'ubuntu'
type: choice
options:
- ubuntu
- rocky
wait:
description: 'Wait for ADO run to complete'
required: true
default: 'true'
type: boolean

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Consider adding concurrency control keyed on cluster input.

The workflow allows parallel manual triggers. If multiple users trigger lifecycle operations (especially reset, create, or delete) on the same cluster simultaneously, they could interfere with each other.

♻️ Proposed addition of concurrency group

Add this block after the on: section and before jobs::

       wait:
         description: 'Wait for ADO run to complete'
         required: true
         default: 'true'
         type: boolean
 
+concurrency:
+  group: edge-cluster-${{ inputs.cluster }}
+  cancel-in-progress: false
+
 jobs:
   trigger:

This allows parallel operations on different clusters while serializing operations on the same cluster. Set cancel-in-progress: true if you prefer newer runs to cancel pending ones.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
on:
workflow_dispatch:
inputs:
cluster:
description: 'Edge cluster slot'
required: true
default: 'edge4'
type: choice
options:
- edge4
- k3s-1
- k3s-2
- rocky-1
action:
description: 'Lifecycle action'
required: true
default: 'plan'
type: choice
options:
- plan
- create
- delete
- reset
os:
description: 'Base OS'
required: true
default: 'ubuntu'
type: choice
options:
- ubuntu
- rocky
wait:
description: 'Wait for ADO run to complete'
required: true
default: 'true'
type: boolean
on:
workflow_dispatch:
inputs:
cluster:
description: 'Edge cluster slot'
required: true
default: 'edge4'
type: choice
options:
- edge4
- k3s-1
- k3s-2
- rocky-1
action:
description: 'Lifecycle action'
required: true
default: 'plan'
type: choice
options:
- plan
- create
- delete
- reset
os:
description: 'Base OS'
required: true
default: 'ubuntu'
type: choice
options:
- ubuntu
- rocky
wait:
description: 'Wait for ADO run to complete'
required: true
default: 'true'
type: boolean
concurrency:
group: edge-cluster-${{ inputs.cluster }}
cancel-in-progress: false
jobs:
trigger:
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 13-48: insufficient job-level concurrency limits (concurrency-limits): workflow is missing concurrency setting

(concurrency-limits)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/edge-cluster-trigger.yml around lines 13 - 48, Add a
concurrency group to the workflow to prevent simultaneous lifecycle actions on
the same cluster by using the cluster input as the key; specifically insert a
concurrency block after the on: section (before jobs:) that sets group to
something like "edge-cluster-${{ github.workflow }}-${{
github.event.inputs.cluster }}" (or similar using inputs.cluster) and optionally
set cancel-in-progress: true to let newer runs cancel older ones; this will
serialize runs for the same cluster while still allowing parallel runs for
different clusters and references the existing inputs.cluster and the workflow's
trigger.

Comment on lines +73 to +84
- id: ado
uses: k8sstormcenter/actions/trigger-ado-pipeline@main
with:
org: ${{ vars.ADO_ORG }}
project: ${{ vars.ADO_PROJECT }}
pipeline_id: ${{ vars.ADO_PIPELINE_EDGE }}
pat: ${{ secrets.ADO_PAT }}
cluster: ${{ inputs.cluster }}
action: ${{ inputs.action }}
os: ${{ inputs.os }}
wait: ${{ inputs.wait }}
timeout: '1800'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the latest commit hash for the action to pin to

gh api repos/k8sstormcenter/actions/commits/main \
  --jq '.sha' \
  | head -c 7

Repository: k8sstormcenter/node-agent

Length of output: 76


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Show the exact workflow lines around the ADO trigger step
echo "---- Workflow snippet ----"
nl -ba .github/workflows/edge-cluster-trigger.yml | sed -n '60,95p'

# 2) Get full SHA for k8sstormcenter/actions@main
echo "---- GitHub: full SHA for repos/k8sstormcenter/actions@main ----"
FULL_SHA="$(gh api repos/k8sstormcenter/actions/commits/main --jq '.sha')"
echo "$FULL_SHA"

# 3) Verify that trigger-ado-pipeline path exists in that repo at that ref
# (GitHub Actions expects an action.yml/metadata inside the directory for the path portion.)
echo "---- GitHub: check contents/trigger-ado-pipeline exists at ref ----"
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline?ref=$FULL_SHA" --jq '.[].path' || \
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline?ref=$FULL_SHA" --jq '.path'

Repository: k8sstormcenter/node-agent

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- Workflow snippet (lines 65-95) ----"
awk 'NR>=65 && NR<=95 {printf "%d:%s\n", NR, $0}' .github/workflows/edge-cluster-trigger.yml

echo "---- GitHub: full SHA for repos/k8sstormcenter/actions@main ----"
FULL_SHA="$(gh api repos/k8sstormcenter/actions/commits/main --jq '.sha')"
SHORT_SHA="$(echo "$FULL_SHA" | head -c 7)"
echo "FULL_SHA=$FULL_SHA"
echo "SHORT_SHA=$SHORT_SHA"

echo "---- GitHub: check trigger-ado-pipeline directory exists at that ref ----"
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline?ref=$FULL_SHA" --jq '{path, type, name}' || true

echo "---- GitHub: check action entry files exist at that ref ----"
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline/action.yml?ref=$FULL_SHA" --jq '.path' || true
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline/action.yaml?ref=$FULL_SHA" --jq '.path' || true
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline/metadata.json?ref=$FULL_SHA" --jq '.path' || true

Repository: k8sstormcenter/node-agent

Length of output: 2318


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FULL_SHA="$(gh api repos/k8sstormcenter/actions/commits/main --jq '.sha')"
echo "FULL_SHA=$FULL_SHA"

echo "---- Find any paths containing 'trigger-ado' in that ref ----"
gh api "repos/k8sstormcenter/actions/git/trees/$FULL_SHA?recursive=1" --jq '
  .tree[]
  | select(.path | test("trigger-ado"; "i"))
  | {path, type}
' | head -n 50

echo "---- Find exact 'trigger-ado-pipeline' matches ----"
gh api "repos/k8sstormcenter/actions/git/trees/$FULL_SHA?recursive=1" --jq '
  .tree[]
  | select(.path == "trigger-ado-pipeline" or .path | test("trigger-ado-pipeline"; "i"))
  | {path, type}
' | head -n 50

Repository: k8sstormcenter/node-agent

Length of output: 180


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- contents: trigger-ado-pipeline at ref=main ----"
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline?ref=main" --jq '
  if type=="array" then map({name, path, type}) else {path, type} end
' || true

echo "---- contents: trigger-ado-pipeline at ref=SHORT_SHA=27790fc (if needed) ----"
gh api "repos/k8sstormcenter/actions/contents/trigger-ado-pipeline?ref=27790fc" --jq '
  if type=="array" then map({name, path, type}) else {path, type} end
' || true

Repository: k8sstormcenter/node-agent

Length of output: 508


Pin trigger-ado-pipeline to a commit SHA (remove @main).

uses: k8sstormcenter/actions/trigger-ado-pipeline@main lets the workflow automatically pick up whatever main points to next. Pin it to 27790fc0392ded46e74bf2d7620c0b790cde2681 (e.g., update the uses: line around line 74). [Optional: keep timeout: '1800' as-is since it’s reasonable for a smoke test.]

🧰 Tools
🪛 zizmor (1.25.2)

[error] 74-74: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/edge-cluster-trigger.yml around lines 73 - 84, Update the
GitHub Action step that defines the "ado" job to pin the external action instead
of using the floating ref: replace the uses string
"k8sstormcenter/actions/trigger-ado-pipeline@main" (in the step with id: ado)
with the commit SHA
"k8sstormcenter/actions/trigger-ado-pipeline@27790fc0392ded46e74bf2d7620c0b790cde2681";
keep the other inputs (org, project, pipeline_id, pat, cluster, action, os,
wait, timeout) unchanged.

Comment on lines +86 to +95
- name: Summary
if: always()
run: |
echo "### ADO run" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "- cluster: \`${{ inputs.cluster }}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- action: \`${{ inputs.action }}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- run id: ${{ steps.ado.outputs.run_id }}" >> "$GITHUB_STEP_SUMMARY"
echo "- url: ${{ steps.ado.outputs.run_url }}" >> "$GITHUB_STEP_SUMMARY"
echo "- result: ${{ steps.ado.outputs.result }}" >> "$GITHUB_STEP_SUMMARY"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Mitigate potential code injection via step outputs.

Lines 93-95 expand step outputs directly in the shell script. While the outputs come from a trusted internal action, if they contain shell metacharacters (backticks, $(), etc.), they could execute unintended code. Lines 91-92 are safe because workflow inputs are constrained by the choice type.

🛡️ Proposed fix using environment variables
       - name: Summary
         if: always()
+        env:
+          RUN_ID: ${{ steps.ado.outputs.run_id }}
+          RUN_URL: ${{ steps.ado.outputs.run_url }}
+          RESULT: ${{ steps.ado.outputs.result }}
         run: |
           echo "### ADO run" >> "$GITHUB_STEP_SUMMARY"
           echo "" >> "$GITHUB_STEP_SUMMARY"
           echo "- cluster: \`${{ inputs.cluster }}\`" >> "$GITHUB_STEP_SUMMARY"
           echo "- action:  \`${{ inputs.action }}\`"  >> "$GITHUB_STEP_SUMMARY"
-          echo "- run id:  ${{ steps.ado.outputs.run_id }}" >> "$GITHUB_STEP_SUMMARY"
-          echo "- url:     ${{ steps.ado.outputs.run_url }}" >> "$GITHUB_STEP_SUMMARY"
-          echo "- result:  ${{ steps.ado.outputs.result }}" >> "$GITHUB_STEP_SUMMARY"
+          echo "- run id:  $RUN_ID" >> "$GITHUB_STEP_SUMMARY"
+          echo "- url:     $RUN_URL" >> "$GITHUB_STEP_SUMMARY"
+          echo "- result:  $RESULT" >> "$GITHUB_STEP_SUMMARY"
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 91-91: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[warning] 92-92: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[info] 93-93: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[info] 94-94: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[info] 95-95: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/edge-cluster-trigger.yml around lines 86 - 95, The Summary
step currently injects step outputs directly into a shell script which can allow
shell metacharacters to be interpreted; instead, capture the ADO outputs into
environment variables (e.g., ADO_RUN_ID, ADO_RUN_URL, ADO_RESULT from
steps.ado.outputs.run_id/run_url/result) and then append them to
GITHUB_STEP_SUMMARY using safe, quoted expansion or printf (e.g., use printf
'%s\n' "$ADO_RUN_ID") so that backticks/$() are not executed; modify the
"Summary" run block to export/use those env vars and ensure all variable
expansions are quoted when writing to GITHUB_STEP_SUMMARY.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant