Owl.System (Owl v0.13.1)

Copy Markdown View Source

Alternatives to selected System functions with safer logging.

Summary

Functions

A wrapper around System.cmd/3 that additionally logs the executed command, args, and env.

Runs command as a daemon, executes operation, and then terminates the daemon.

A wrapper around System.shell/2 that additionally logs the executed command.

Functions

cmd(command, args, opts \\ [])

@spec cmd(
  binary(),
  [binary() | {:secret, binary()} | [binary() | {:secret, binary()}]],
  keyword()
) :: {Collectable.t(), exit_status :: non_neg_integer()}

A wrapper around System.cmd/3 that additionally logs the executed command, args, and env.

If a URL is found in the logged message, the password in it is masked with asterisks. You can explicitly mark environment values and arguments as secret for safe logging. See the examples for details.

Examples

> Owl.System.cmd("echo", ["test"])
# 10:25:34.252 [debug] $ echo test
{"test\n", 0}

# marking an argument as secret
> Owl.System.cmd("echo", ["hello", secret: "world"])
# 10:25:40.516 [debug] $ echo hello ********
{"hello world\n", 0}

# marking a part of an argument as secret
> Owl.System.cmd("echo", ["hello", ["--password=", {:secret, "world"}]])
# 10:25:40.516 [debug] $ echo hello --password=********
{"hello --password=world\n", 0}

# marking an env as secret
> Owl.System.cmd("echo", ["hello", "world"], env: [{"PASSWORD", {:secret, "mypassword"}}])
# 10:25:40.516 [debug] $ PASSWORD=******** sh -c "echo hello world"
{"hello world\n", 0}

> Owl.System.cmd("psql", ["postgresql://postgres:postgres@127.0.0.1:5432", "-tAc", "SELECT 1;"])
# 10:25:50.947 [debug] $ psql postgresql://postgres:********@127.0.0.1:5432 -tAc 'SELECT 1;'
{"1\n", 0}

daemon_cmd(command, args, operation, options \\ [])

@spec daemon_cmd(
  binary(),
  [binary() | {:secret, binary()} | [binary() | {:secret, binary()}]],
  (-> result),
  prefix: Owl.Data.t(),
  device: IO.device(),
  ready_check: (String.t() -> boolean()),
  env: [{binary(), binary() | {:secret, binary()} | nil}]
) :: result
when result: any()

Runs command as a daemon, executes operation, and then terminates the daemon.

Automatically writes messages from stderr and stdout to device, prefixing them with prefix. Returns the result of invoking operation.

Options

  • :prefix - a prefix for stderr and stdout messages from the daemon. Defaults to command followed by a colon.
  • :device - the device to which messages from stderr and stdout are written. Defaults to :stdio.
  • :ready_check - a function that examines messages produced by command before writing to device. If set, execution of operation is blocked until ready_check returns true. By default this check is absent, and operation is invoked immediately without waiting for any message.
  • :env - a list of environment key-value tuples. Behaviour is similar to the option described in cmd/3.

Example

ex> Owl.System.daemon_cmd("ping", ["8.8.8.8"], fn ->
..>   Process.sleep(3_000)
..>   2 + 2
..> end)
# 00:36:33.963 [debug] $ ping 8.8.8.8
#
# 00:36:33.964 [debug] Started daemon ping with OS pid 576077
# ping:  PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
# ping:  64 bytes from 8.8.8.8: icmp_seq=1 ttl=118 time=28.3 ms
# ping:  64 bytes from 8.8.8.8: icmp_seq=2 ttl=118 time=26.9 ms
# ping:  64 bytes from 8.8.8.8: icmp_seq=3 ttl=118 time=28.6 ms

# 00:36:36.965 [debug] $ kill 576077
4

ex> Owl.System.daemon_cmd(
..>   "kubectl",
..>   [
..>     "port-forward",
..>     "--namespace=my-app",
..>     "--kubeconfig",
..>     "~/.kube/myapp",
..>     "my-pod",
..>     "5432:5432"
..>   ],
..>   fn ->
..>     Logger.debug("Dump DB")
..>   end,
..>   prefix: "kubectl(my-pod): ",
..>   ready_check: &String.contains?(&1, "Forwarding from")
..> )
# 12:08:01.382 [debug] $ kubectl port-forward --namespace=my-app --kubeconfig ~/.kube/myapp my-pod 5432:5432
# 12:08:01.384 [debug] Started daemon kubectl with OS pid 166378
# kubectl(my-pod): Forwarding from 127.0.0.1:5432 -> 5432
# kubectl(my-pod): Forwarding from [::1]:5432 -> 5432
# 12:08:02.484 [debug] Dump DB
# 12:08:02.584 [debug] $ kill 166378
:ok

shell(command, opts \\ [])

@spec shell(
  binary(),
  keyword()
) :: {Collectable.t(), exit_status :: non_neg_integer()}

A wrapper around System.shell/2 that additionally logs the executed command.

Similar to cmd/3, it automatically hides passwords in URLs and allows manually hiding environment values.

Examples

> Owl.System.shell("echo hello world")
# 22:36:01.440 [debug] $ sh -c "echo hello world"
{"hello world\n", 0}

> Owl.System.shell("echo postgresql://postgres:postgres@127.0.0.1:5432")
# 22:36:51.797 [debug] $ sh -c "echo postgresql://postgres:********@127.0.0.1:5432"
{"postgresql://postgres:postgres@127.0.0.1:5432\n", 0}

> Owl.System.shell("echo $PASSWORD $USERNAME", env: [{"USERNAME", "john"}, {"PASSWORD", {:secret, "qwerty"}}])
# 12:38:27.704 [debug] $ USERNAME=john PASSWORD=******** sh -c "echo \$PASSWORD \$USERNAME"
{"qwerty john\n", 0}