Skip to content

ci: add manual workflow to start/stop the Scaleway cluster #25

Description

@nbrieussel

Context / problem

cluster/scaleway/ can only be brought up or torn down from a developer's laptop
via mise run scaleway-up / scaleway-destroy, which needs a fully-configured
local toolchain and privileged creds. We want to start/stop the homelab cluster
on demand from GitHub Actions — operable from anywhere, auditable, not
machine-bound — now that the github-ci Scaleway identity exists.

Blocker: the current github-ci key is scoped to ObjectStorageReadOnly (a
connectivity PoC), which cannot provision a Kapsule cluster. So this change
also broadens that policy.

Scope

  • New manually-triggered workflow (workflow_dispatch) with a required
    action input: up (full scaleway-up: cluster + node pool + ArgoCD) or
    destroy (full terraform destroy).
  • Broaden the github-ci IAM policy so the existing SCW_* secrets can drive
    a cluster apply/destroy; keep it project-scoped.
  • Wire Infisical machine-identity creds into CI (needed for the ArgoCD bootstrap
    on up).
  • Update github-ci/README.md to match the broadened privilege.

Non-goals: scheduled/cron start-stop; node_count pause/resume (stays local);
keyless OIDC→Scaleway (blocked upstream); any change to cluster/local/ or the
gitops repo.

Acceptance criteria

  • Workflow triggers only on workflow_dispatch, not push/PR; action
    input accepts exactly up / destroy.
  • action=up runs the equivalent of mise run scaleway-up (init +
    apply -auto-approve, bootstrap_argocd=true).
  • action=destroy runs terraform destroy -auto-approve and doesn't hang on
    confirmation.
  • Uses the Terraform version pinned in mise.toml.
  • Both the Scaleway provider and the S3 state backend authenticate from
    the github-ci key; missing creds fail fast; nothing secret is logged.
  • up supplies Infisical client_id/client_secret so the ArgoCD bootstrap
    succeeds.
  • The broadened IAM policy lets Terraform create the VPC/Kapsule/node pool and
    write state, while staying scoped to var.project_id.
  • github-ci/README.md no longer claims the identity is read-only /
    least-privilege read-only.

Design notes

  • Single workflow + action choice input (not two workflows).
  • Configure ~/.config/scw/config.yaml from secrets, then run the mise task.
    Critical subtlety: mise.toml's [env] derives AWS_ACCESS_KEY_ID/SECRET
    from scw config get …; if you only export SCW_* env vars, mise blanks out
    the backend's AWS creds. Writing the scw config makes both provider and backend
    authenticate with zero drift from local behaviour. Install the toolchain via
    jdx/mise-action (pin by commit SHA) so Terraform matches mise.toml.
  • destroy needs -auto-approve — the existing scaleway-destroy task lacks
    it. Run terraform directly for the destroy branch (or add a scaleway-destroy-ci
    task); don't add -auto-approve to the local scaleway-destroy task.
  • Permission sets (verified via scw iam permission-set list, all
    project-scoped): KubernetesFullAccess, VPCFullAccess, and
    ObjectStorageReadOnly + ObjectStorageObjectsWrite (read and write the
    state object) — or the simpler single ObjectStorageFullAccess. The cluster
    root creates no IAM resources, so no IAM permission set is needed.
  • Safety: environment: scaleway + permissions: contents: read + a
    fail-fast credential assertion (mirrors .github/workflows/scaleway-auth-check.yml,
    the reference workflow); optional typed-confirmation (e.g. homelab) for
    destroy.

Tasks

1. Broaden the github-ci IAM policy

  • 1.1 In github-ci/main.tf, replace the scaleway_iam_policy.github_ci
    rule's permission_set_names = ["ObjectStorageReadOnly"] with the sets
    above. Keep project_ids = [var.project_id].
  • 1.2 Update the policy resource name/description (currently
    github-ci-object-storage-ro) and surrounding comments to reflect the
    broadened scope.
  • 1.3 mise run github-ci-plan and review; do NOT apply without explicit
    approval (billable identity change).

2. Wire CI credentials

  • 2.1 Confirm/refresh SCW_ACCESS_KEY / SCW_SECRET_KEY GitHub secrets from
    the broadened key (Terraform output / Infisical) per github-ci/README.md;
    don't echo values.
  • 2.2 Add Infisical machine-identity GitHub secrets (client_id /
    client_secret) — recommend scoping them to the scaleway environment
    alongside SCW_*. Used as infisical_client_id / infisical_client_secret
    on the up path.

3. Add the lifecycle workflow

  • 3.1 Create .github/workflows/scaleway-cluster-lifecycle.yml, triggered
    only by workflow_dispatch with a required choice input action
    (up | destroy). Set permissions: contents: read and
    environment: scaleway.
  • 3.2 Add a fail-fast step asserting SCW_ACCESS_KEY/SCW_SECRET_KEY (and,
    for up, the Infisical creds) are present.
  • 3.3 Install the pinned toolchain with jdx/mise-action (pinned by SHA).
  • 3.4 Write ~/.config/scw/config.yaml from secrets/variables (access_key,
    secret_key, default_project_idvars.SCW_DEFAULT_PROJECT_ID,
    default_organization_idvars.SCW_DEFAULT_ORGANIZATION_ID). Add a
    comment explaining why env-only is insufficient.
  • 3.5 up branch: export TF_VAR_infisical_client_id /
    TF_VAR_infisical_client_secret and run mise run scaleway-up.
  • 3.6 destroy branch: run a non-interactive destroy (terraform directly via
    mise exec, or a scaleway-destroy-ci task).
  • 3.7 (Optional) gate destroy behind a typed-confirmation input.

4. Docs & validation

  • 4.1 Update github-ci/README.md "What it creates" / least-privilege
    sections to match the broadened permission sets.
  • 4.2 actionlint .github/workflows/scaleway-cluster-lifecycle.yml.
  • 4.3 Run mise run lock only if provider constraints changed (not expected).

5. End-to-end verification

  • 5.1 Dispatch action=up; confirm cluster + node pool + ArgoCD come up and
    state is written to id-terraform-state.
  • 5.2 Dispatch action=destroy; confirm a clean teardown, no
    permission-denied, job exits non-zero on any Terraform failure.

Constraints

  • Target repo: infrastructure. Branch ci/<desc>, draft PR to main.
  • Never terraform apply/destroy the github-ci root without explicit approval
    (billable identity change).
  • up/destroy mutate the live, billable Scaleway cluster and its remote
    state — destructive by design; hence manual + environment-gated.
  • No secret values in code or the PR.

Open questions (dev agent's call)

  • Granular object-storage sets vs single ObjectStorageFullAccess.
  • Whether to add the typed-confirmation guard for destroy.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions