Iterate on a Machine on kind
Change a Machine, converge the change into your kind cluster, and run it again.
kind runs a Kubernetes cluster on your computer, and jr2 uses it as its local cluster. The loop has three steps: edit the file, run jr2 up, and run the Workflow. The Orchestrator runs only the Machines that the last jr2 up gave it. Thus a change that you save does nothing until you converge it.
The examples on this page use the kind cluster jr2 and the Instance my-instance from Get started. They change ping, the Workflow that jr2 init writes. It needs no model.
Change the Machine
Open workflows/ping.ts. In the line of the respond actor, change `pong: ${input.message}` to `pong, again: ${input.message}`. Save the file.
To find the errors before you converge, run jr2 check. jr2 up runs the same check first:
jr2 checkIt typechecks the Instance, loads each Workflow and walks its Machine, with no cluster. It prints the typecheck, the effects: lines, the seats: lines and the failures: lines, and then check: passed. When it refuses, it gives the line that jr2 up gives, and exits with 1. npm run typecheck runs only the compiler, so it does not find a refusal that occurs when a module loads. The checks of git.credentials and of the images need the cluster, so only jr2 up does them.
Converge the change
Run jr2 up in the Instance folder:
jr2 upFor an Instance that has only ping, the output is:
jr2 up — instance "my-instance" → context kind-jr2 / namespace my-instance
typecheck: tsc --noEmit (the instance's own compiler)
agents: (none) (carried by 1 workflow machine(s))
assembly: staging
assembly: 0adf5bff234d (kit v<version>; 1 workflow(s): ping)
…
sandbox images: skipped (no registered Machine composes a Sandbox)
…
orchestrator: applying (image ghcr.io/snapwich/jr2-orchestrator:<version>)
orchestrator: waiting for rollout
orchestrator: verified — 1 pod(s) running ghcr.io/snapwich/jr2-orchestrator:<version>
instance effector: none (no Effect is placed there)
assembly: publishing 0adf5bff234d (current is ba71fa27f37f)
assembly: 0adf5bff234d is current — workflows: ping
swept nothing — every labeled image is named by a live root
converged — `jr2 run <workflow>` when ready0adf5bff234d is the hash of the new Assembly, and ba71fa27f37f is the hash of the Assembly that was current. Your hashes can be different. <version> is the Kit version. The hash changed, because the Workflow changed. Thus jr2 up published the new Assembly to the Orchestrator. The image of the Orchestrator did not change, so its pod did not start again. Your output has more lines when your Machines use a model, a Sandbox or an Effect.
jr2 up says converged only when the Orchestrator made the new Assembly current. When it stops with refusing:, the Assembly that was current stays current. In both cases, fix the cause, then run jr2 up again. Fix a failed converge gives each refusal and its fix.
Run it again
Run the Workflow:
jr2 run ping --input '{"message":"hi"}'→ context kind-jr2 / namespace my-instance
run 7df09f94-d26d-44c3-ba1e-0279f16ccacb (ping) — attached
→ done "done"
{"runId":"7df09f94-d26d-44c3-ba1e-0279f16ccacb","workflow":"ping","instanceId":"b2ec6504-553e-495a-a8be-62715c6d8595","status":"done","value":"done","context":{"message":"hi","reply":"pong, again: hi"},"children":[],"waiting":[],"held":[],"assembly":"0adf5bff234d"}Your run id and your instance id are different. The reply comes from the new Machine, and assembly is the hash of the new Assembly.
The pod of the Orchestrator did not start again, so the port forward of the Console stays open. Load the page again. Then the Console draws the new Machine. Open the Console gives the command for the port forward.
Why a repeated jr2 up is fast
jr2 up does the full converge each time, but it builds a Sandbox Image only when its folder changed, and it publishes the Assembly only when its hash changed. It never builds a Kit image, such as the image of the Orchestrator: the cluster pulls each one at the Kit version. Converge tells why. You see it in these lines:
(fresh — build skipped; --force to rebuild anyway): the tag of a Sandbox Image did not change, sojr2 updid not build it.(host-built — build skipped; --force to rebuild): your computer holds the Sandbox Image with that tag, sojr2 updid not build it.(current — publish skipped): the Orchestrator already has the Assembly with this hash.
A second jr2 up with no change builds nothing and publishes nothing:
…
assembly: 0adf5bff234d (current — publish skipped)
swept nothing — every labeled image is named by a live root
converged — `jr2 run <workflow>` when readyTo build all the images again, run jr2 up --force. It builds an image again also when its tag did not change. It also sends the Assembly again, and the Orchestrator answers that the Assembly did not change.
The runs that are live when you converge
A publish does not start the pod of the Orchestrator again. A run that is live keeps the Assembly that it started under. Thus it continues on the old Machine, to its end, also after a restart. A run that waits at a Gate still waits there, and its Sandbox stays. A jr2 run that is attached stays attached. Each new run uses the new Machine.
jr2 status, with no run id, shows each Assembly that a live run holds:
jr2 statusIn this output, a run of the Workflow rollout from Write a Workflow waits at its Gate. It started before the last jr2 up:
→ context kind-jr2 / namespace my-instance
assembly: a44731886e21 (kit v<version>, current), 0 live runs hold it
assembly: 3a3fa3c403f9 (kit v<version>), 1 live run holds it
data plane: yesThe Orchestrator removes the old Assembly when the last run that holds it ends. data plane: yes tells that the Orchestrator runs in a cluster, so it can make Sandboxes.
A live run cannot continue when the Orchestrator cannot read its Assembly: the volume lost it, or it is for a different Kit version. Then the Orchestrator keeps the run with the status drifted, and jr2 status <run id> gives the reason. Upgrade the Kit version tells when this occurs.
Rules
jr2 upchanges nothing in the cluster when the Instance does not typecheck:refusing: this instance does not typecheck — nothing was built and nothing was applied. The output of the compiler follows, one line for each error.jr2 upchanges nothing in the cluster when a registered Machine has an Open part that nobody bound. It prints the line that binds it:refusing: workflow "<name>" leaves <part> open — bind it where the Machine is registered: export const machine = customize(<import>, <binding>) (<adr>). Bind a shipped Machine's Open parts tells how.jr2 upprints an error in a Workflow module aserror: <message>, and exits with1. It changes nothing in the cluster. Write a Workflow gives each error.jr2 uprefuses when kubectl has no current context:error: no kube context — `kind create cluster` (or point kubectl at one), then re-run `jr2 up`jr2 uploads the Sandbox Images into the nodes only on a kind cluster: a context whose name starts withkind-. For a different context with noregistry, it builds nothing:error: context <context> is not a kind cluster and no `registry` is configured — set `registry` in jr2.config.ts (from env) so the image can be pushed (ADR-0019)- The Orchestrator refuses an Assembly that fails its checks.
jr2 upprints the reason and the lines of the report, and the Assembly that was current stays current:refusing: the Orchestrator refused Assembly <hash> (HTTP <status>): <error> — the previous Assembly is still current
Read next
- Converge
- Durable runs: the Machine shape and drift.
- ADR-0019: one converging command against the current context.
- ADR-0069: a publish loads the Assembly with no restart, and a run keeps its Assembly.
- ADR-0038:
jr2 upbuilds each image that the cluster runs, with a content hash as its tag. - ADR-0041: a build that your computer holds is not done again.
- ADR-0050: the typecheck is the first step of
jr2 up. - Write a Workflow
- Move from kind to a shared cluster
- Reference:
jr2 up,jr2 run,jr2 status