Macaw.Host.Release (macaw_host v0.1.4)

Copy Markdown View Source

The release side of an app's <app>-ctl: entry points the script runs as bin/<app> eval (in a throwaway container, so they work with the app stopped) or bin/<app> rpc (asking the running app).

The protocol, the same for every app:

  • Arguments arrive as a JSON array in <PREFIX>_CTL_ARGS, never on a command line ps can show; a password in <PREFIX>_CTL_PASSWORD.
  • Output is one JSON object per line on stdout, for the script to read; a failure is one line on stderr and exit status 1.
  • Nothing typed becomes an atom. Names are matched as strings (String.to_existing_atom/1 fails under eval for atoms the release never loaded, though tests pass: the bug this module exists to rule out).

An app's release module delegates to these:

defmodule MyApp.Release do
  def migrate, do: Macaw.Host.Release.migrate(:my_app)
  def config, do: Macaw.Host.Release.config(:my_app, MyApp.Settings.specs(), "MY_APP")
  def status, do: Macaw.Host.Release.status(MyApp.Health.report())
end

Summary

Functions

<app>-ctl config list | get NAME | set NAME VALUE | unset NAME, over the app's literal config.exs in its data directory (:data_dir in the app's environment). Arguments from <PREFIX>_CTL_ARGS.

The config commands, pure apart from the file and env (the environment, System.get_env/0 by default): {:ok, objects} or {:error, message}. A file with faults is the error, every fault on one line.

The arguments in <PREFIX>_CTL_ARGS, a JSON array of strings. Fails with a message when it is missing or malformed.

The password in <PREFIX>_CTL_PASSWORD, or nil.

Prints one JSON object as a line.

Prints a message on stderr and exits with status 1.

Runs every repo's migrations for app (its :ecto_repos), starting only what migrating needs. Requires ecto_sql.

Prints the running app's report (a health map, say) as one JSON line, for rpc.

Functions

config(app, specs, prefix)

@spec config(atom(), [Macaw.Host.Config.spec()], String.t()) :: :ok

<app>-ctl config list | get NAME | set NAME VALUE | unset NAME, over the app's literal config.exs in its data directory (:data_dir in the app's environment). Arguments from <PREFIX>_CTL_ARGS.

config_command(path, specs, prefix, argv, env \\ System.get_env())

@spec config_command(
  Path.t(),
  [Macaw.Host.Config.spec()],
  String.t(),
  [String.t()],
  map()
) ::
  {:ok, [map()]} | {:error, String.t()}

The config commands, pure apart from the file and env (the environment, System.get_env/0 by default): {:ok, objects} or {:error, message}. A file with faults is the error, every fault on one line.

list and get show each setting as the app would load it, through Macaw.Host.Config.resolve_setting/4, the path Macaw.Host.Config.load!/3 takes: %{name, value, source}, the value typed and then shown (Macaw.Host.Config.mask/2), or, when it doesn't fit its type, the value as found with the load's fault.

ctl_args!(prefix)

@spec ctl_args!(String.t()) :: [String.t()]

The arguments in <PREFIX>_CTL_ARGS, a JSON array of strings. Fails with a message when it is missing or malformed.

ctl_password(prefix)

@spec ctl_password(String.t()) :: String.t() | nil

The password in <PREFIX>_CTL_PASSWORD, or nil.

emit(object)

@spec emit(map()) :: :ok

Prints one JSON object as a line.

fail(message)

@spec fail(String.t()) :: no_return()

Prints a message on stderr and exits with status 1.

migrate(app)

@spec migrate(atom()) :: :ok

Runs every repo's migrations for app (its :ecto_repos), starting only what migrating needs. Requires ecto_sql.

status(report)

@spec status(map()) :: :ok

Prints the running app's report (a health map, say) as one JSON line, for rpc.