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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions