Wanderland

K8s Pack: the kustomize/kubectl deploy slot

wanderland-k8s-pack ships the deploy-slot boundaries for a site whose target is a Kubernetes cluster. It fills the deploy slot of the containerized_deployment archetype — the same slot cfn_provisioner fills for an ECS/CloudFormation target — with the kustomize/kubectl path: pin an overlay to the freshly-built image, apply it, and roll the deployment. The boundaries follow the iscanGuide deploy step by step (kustomize edit set image → kubectl apply → rollout restart → rollout status), reframed as wanderland boundaries that run through the :kubectl shell adapter.

The pack ships no archetype of its own. A Kubernetes deployment names containerized_deployment like any other, builds its image in the image stage, and names k8s_provisioner for the deploy component. The image the deploy pins comes from the build slot's output, threaded in with !Context.

What the pack ships

Add the gem; the entry file requires wanderland-core and globs the three boundaries, which self-register on load. There is no AWS dependency.

# Gemfile
gem "wanderland-k8s-pack", path: "../wanderland-k8s-pack"
Boundary Role What it does
k8s_provisioner deploy slot wrapper Pin the overlay image, then apply + rollout, as one group. The kubectl analogue of cfn_provisioner.
kustomize_set_image sub-step (pure transform) Write the images: entry in an overlay's kustomization.yaml so it points at a specific image ref. No binary, no adapter.
kubectl_apply sub-step (shells out) Render + apply the overlay, then rollout restart a deployment and wait for rollout status.

The pack needs one adapter: a :kubectl shell mount.

adapters:
  kubectl:
    kind: shell
    command: kubectl

kind: shell wraps one fixed binary behind .run(*args, cwd:, env:), returning {exit_code:, stdout:, stderr:, duration_s:}. The same live / replay / mock discipline as every other adapter applies: a scenario records the kubectl calls to cassettes once, then replays them.

The deploy slot: k8s_provisioner

k8s_provisioner is the deploy-slot wrapper. It dispatches kustomize_set_image to pin the overlay, then kubectl_apply to apply and roll out, exposing both sub-boundaries' args through the parent. A containerized_deployment component names it like this:

pipeline:
  stages:
    deploy:
      type: component
      args:
        output_root: /tmp/iscanguide-deploy

  components:
    sc-website:
      type: k8s_provisioner
      args:
        kustomize_dir: k8s/overlays/prod
        name:          789905347053.dkr.ecr.us-west-2.amazonaws.com/iscanguide/sc-website
        image:         !Context for_boundary.sc-website.image_ref
        namespace:     iscanguide
        deployment:    deploy/sc-website
        wait_timeout_s: 180
        load_restrictor: LoadRestrictionsNone

Anatomy:

k8s_provisioner dispatches its sub-boundaries through input.dispatch, so their results roll up under its crossing and a sub-boundary's halt propagates as the wrapper's halt. The route sees one signed crossing for the deploy; the set-image and apply steps are its internals.

Pinning the image: kustomize_set_image

kustomize_set_image writes the images: block in the overlay's kustomization.yaml. It is a pure YAML transform — no kustomize binary, no adapter — so it is deterministic and unit-testable. A kustomize overlay's images: block looks like this before a deploy:

# k8s/overlays/prod/kustomization.yaml
images:
  - name: 789905347053.dkr.ecr.us-west-2.amazonaws.com/iscanguide/sc-website
    newTag: latest
  - name: 789905347053.dkr.ecr.us-west-2.amazonaws.com/iscanguide/sc-worker
    newTag: latest

Given name and a full image ref, the boundary finds (or appends) the matching entry and writes newName + newTag. It splits the ref on its final : into name and tag, treating the colon as a tag separator only when the suffix has no / — so a registry port like host:5001/repo keeps its colon and a missing tag clears newTag rather than writing a bogus one. It returns kustomization_path, name, new_name, new_tag, and image. A kustomization.yaml (or .yml) missing under kustomize_dir halts with a 404 naming the directory.

Applying and rolling out: kubectl_apply

kubectl_apply shells through :kubectl to apply the overlay and, when a deployment: is named, restart it and wait for the rollout to stabilize. It accumulates duration_s across every shell call.

It returns applied, kustomize_dir, rolled, rollout_status, and duration_s. k8s_provisioner folds these into its own OK payload alongside the pinned image.

What you have now

A site whose target is Kubernetes can name containerized_deployment, build its image in the image stage, and name k8s_provisioner for each deploy component. The provisioner pins each overlay to the image the build just produced, applies it (honoring cross-directory overlays), rolls the deployment, and waits for it to stabilize — all through the :kubectl shell adapter, recordable and replayable like every other adapter. Swapping an ECS target for a Kubernetes one is a change of which boundary fills the deploy slot; the rest of the chain — start_run, collect, load_metadata, image — is unchanged.

Pointers