YmerNode.Script.Harness (Ymer Node v0.2.3)

Copy Markdown View Source

What a VM running script code outside the node needs, for a test or a live run: the processes a script's run reaches for, and the three configuration keys the node's own environments would otherwise set.

A repository that develops scripts of its own takes ymer_node as a dependency under runtime: false — the node's code compiles there, and none of its applications start. A script run inside the node meets a supervision tree and a configuration the node's release, config/dev.exs or config/test.exs arranged; the same script compiled in that repository meets neither. This module is the supported way to arrange both, and it carries no ExUnit, so a live run's own task calls it as readily as a test does. YmerNode.Script.Test is the ExUnit layer over it.

A live run

run_tree/0 is a child list, never a started tree: the caller starts it under a supervisor of its own, once for the whole run, and names that supervisor. Once per run is what a live run against a real system wants — every script declaring one throttle shares its bucket and its breaker across every module of the run, so an account that locks after repeated failed logins meets the breaker's threshold once, not once per module.

{:ok, _pid} =
  Supervisor.start_link(YmerNode.Script.Harness.run_tree(),
    strategy: :one_for_one,
    name: MyScripts.RunTree
  )

The three setters then point the keys a run reads at the real network, the real secrets file and a real directory, for the rest of the VM:

YmerNode.Script.Harness.put_request_plug(nil)
YmerNode.Script.Harness.put_secrets_path(Path.join(install, "data/secrets.env"))
YmerNode.Script.Harness.put_files_dir(Path.join(install, "data/files"))

Whether the run is set up is the caller's own question: Process.whereis/1 of the name it gave its supervisor, and the getters the keys already have — YmerNode.Secrets.path/0, YmerNode.Script.Context.request_options/0 and YmerNode.Script.Context.files_dir/1.

None of this is rendered into scripts guide: a script never learns where its secrets live or what its requests are stubbed with. A script that calls this module from inside the node reaches past its context into the node's own modules — outside the promise, and acceptance is where a reader sees it.

Summary

Functions

Points YmerNode.Script.Context.files_dir/1 at directory, expanded to an absolute path, for the rest of the VM. The directory is the caller's to create, as the node creates its own at boot.

Puts plug into the Req options the node merges under every script request, for the rest of the VM — a stub such as {Req.Test, YmerNode.Script.Context} for a test, or nil to take the plug off, so a live run's requests reach the real host. Every other option YmerNode.Script.Context.request_options/0 answers is kept.

Points YmerNode.Secrets at the file path names, expanded to an absolute path, for the rest of the VM: every later YmerNode.Script.Context.secret/2 reads it and every YmerNode.Secrets.set/2 writes it. A test that follows a live run in the same VM still cannot write a real file this way: YmerNode.Script.Test.put_secrets/1 refuses any path outside the project's tmp/ before it writes.

The processes a script's run needs when the node's application is not running — the throttles' process registry, their supervisor, and the compiler's task supervisor — as a child list the caller starts under a supervisor of its own, per test or once for a whole run of many. The runner's own registry of runs in flight is not in it: that is the node's bookkeeping around a run, not part of what a run needs.

Functions

put_files_dir(directory)

Points YmerNode.Script.Context.files_dir/1 at directory, expanded to an absolute path, for the rest of the VM. The directory is the caller's to create, as the node creates its own at boot.

put_request_plug(plug)

Puts plug into the Req options the node merges under every script request, for the rest of the VM — a stub such as {Req.Test, YmerNode.Script.Context} for a test, or nil to take the plug off, so a live run's requests reach the real host. Every other option YmerNode.Script.Context.request_options/0 answers is kept.

put_secrets_path(path)

Points YmerNode.Secrets at the file path names, expanded to an absolute path, for the rest of the VM: every later YmerNode.Script.Context.secret/2 reads it and every YmerNode.Secrets.set/2 writes it. A test that follows a live run in the same VM still cannot write a real file this way: YmerNode.Script.Test.put_secrets/1 refuses any path outside the project's tmp/ before it writes.

run_tree()

The processes a script's run needs when the node's application is not running — the throttles' process registry, their supervisor, and the compiler's task supervisor — as a child list the caller starts under a supervisor of its own, per test or once for a whole run of many. The runner's own registry of runs in flight is not in it: that is the node's bookkeeping around a run, not part of what a run needs.

The names are the node's own and VM-global, so a VM starts the list once at a time: a second start while the first is up fails at the first child already registered — Supervisor.start_link/3 answers {:error, {:shutdown, {:failed_to_start_child, id, {:already_started, pid}}}} and never attempts the children after it. Inside the node the application has already started every one of them.