ExIncus.Instances (ex_incus v1.0.0)

Copy Markdown

Manage instances (containers and virtual machines).

Creating and controlling instances

client = ExIncus.Client.new()

# Create + start a container from the public image server
{:ok, _} = ExIncus.Instances.launch(client, "web1", "debian/12")

{:ok, _} = ExIncus.Instances.stop(client, "web1")
{:ok, _} = ExIncus.Instances.start(client, "web1")
{:ok, _} = ExIncus.Instances.delete(client, "web1")

Waiting

All state-changing calls are asynchronous on the Incus side; by default this module waits for the operation to finish (up to timeout: seconds, default 300). Pass wait: false to get the ExIncus.Operation back immediately instead.

Config and devices

# Merge config keys (other keys are left alone)
ExIncus.Instances.update_config(client, "web1", %{"limits.memory" => "1GiB"})

# Add or replace a NIC with a static address
ExIncus.Instances.set_device(client, "web1", "eth0", %{
  "type" => "nic",
  "network" => "incusbr0",
  "ipv4.address" => "10.140.100.10"
})

Files

ExIncus.Instances.push_file(client, "web1", "/etc/resolv.conf",
  "nameserver 10.140.100.1\n", mode: 0o644)

{:ok, content} = ExIncus.Instances.pull_file(client, "web1", "/etc/hostname")

Commands

{:ok, result} = ExIncus.Instances.exec(client, "web1", ["uname", "-a"])
result.exit_code #=> 0
result.stdout    #=> "Linux web1 ..."

Summary

Functions

Creates an instance from a full InstancesPost params map.

Creates a symlink at link_path pointing to target.

Deletes a (stopped, unless the server allows otherwise) instance.

Deletes a file or empty directory inside the instance.

Runs a command inside the instance and returns an ExIncus.ExecResult.

Freezes (pauses) the instance.

Fetches a single instance.

Fetches a log file by name (e.g. "lxc.log").

Creates (and by default starts) an instance from an image - the equivalent of incus launch.

Lists instances.

Lists the entries of a directory inside the instance.

Lists available log files (as API URLs).

Creates a directory inside the instance. Options: :uid, :gid, :mode.

Partially updates the instance (PATCH). Supplied "config" and "devices" entries are merged into the existing ones; entries you don't mention are kept.

Reads a file from the instance and returns its content as a binary.

Writes a file into the instance, creating or overwriting it.

Removes a device. PATCH can only merge, so this reads the instance and writes back the full configuration without the device (PUT).

Renames a stopped instance.

Restarts the instance. See set_state/4 for options.

Adds or replaces a single device, leaving other devices untouched.

Changes the runtime state. action is one of :start, :stop, :restart, :freeze, :unfreeze.

Starts the instance. See set_state/4 for options.

Fetches the runtime state: status, network interfaces and addresses, resource usage, PIDs.

Stops the instance (gracefully; pass force: true to kill). See set_state/4.

Unfreezes the instance.

Replaces the instance configuration (PUT). params must be a full InstancePut map; for partial changes prefer patch/4 or update_config/4.

Merges the given keys into the instance config, leaving other keys untouched. Values are stringified (the API only accepts strings).

Types

name()

@type name() :: String.t()

result()

@type result() :: {:ok, term()} | {:error, ExIncus.Error.t()}

Functions

create(client, params, opts \\ [])

@spec create(ExIncus.Client.t(), map(), keyword()) :: result()

Creates an instance from a full InstancesPost params map.

You are expected to provide the "source" yourself; for the common image-alias case launch/4 is more convenient.

ExIncus.Instances.create(client, %{
  "name" => "web1",
  "source" => %{
    "type" => "image",
    "protocol" => "simplestreams",
    "server" => "https://images.linuxcontainers.org",
    "alias" => "debian/12"
  },
  "config" => %{"limits.memory" => "1GiB"},
  "profiles" => ["default"]
})

create_symlink(client, name, link_path, target, opts \\ [])

@spec create_symlink(ExIncus.Client.t(), name(), String.t(), String.t(), keyword()) ::
  result()

Creates a symlink at link_path pointing to target.

delete(client, name, opts \\ [])

@spec delete(ExIncus.Client.t(), name(), keyword()) :: result()

Deletes a (stopped, unless the server allows otherwise) instance.

delete_file(client, name, file_path, opts \\ [])

@spec delete_file(ExIncus.Client.t(), name(), String.t(), keyword()) :: result()

Deletes a file or empty directory inside the instance.

exec(client, name, command, opts \\ [])

@spec exec(ExIncus.Client.t(), name(), [String.t()] | String.t(), keyword()) ::
  {:ok, ExIncus.ExecResult.t() | ExIncus.Operation.t()}
  | {:error, ExIncus.Error.t()}

Runs a command inside the instance and returns an ExIncus.ExecResult.

The command is run non-interactively. Output is recorded on the server and fetched (then deleted) once the command finishes. A non-zero exit code is still {:ok, result}; check result.exit_code.

{:ok, %{exit_code: 0, stdout: out}} =
  ExIncus.Instances.exec(client, "web1", ["cat", "/etc/os-release"])

Options

  • :environment - map of environment variables
  • :user / :group - numeric uid/gid to run as
  • :cwd - working directory
  • :record_output - capture stdout/stderr (default true)
  • :cleanup_logs - delete the recorded output from the server after fetching it (default true)
  • :wait / :timeout - operation waiting; with wait: false you get the running ExIncus.Operation and no output handling

freeze(client, name, opts \\ [])

@spec freeze(ExIncus.Client.t(), name(), keyword()) :: result()

Freezes (pauses) the instance.

get(client, name, opts \\ [])

@spec get(ExIncus.Client.t(), name(), keyword()) :: result()

Fetches a single instance.

get_log(client, name, filename, opts \\ [])

@spec get_log(ExIncus.Client.t(), name(), String.t(), keyword()) ::
  {:ok, binary()} | {:error, ExIncus.Error.t()}

Fetches a log file by name (e.g. "lxc.log").

launch(client, name, image, opts \\ [])

@spec launch(ExIncus.Client.t(), name(), String.t() | map(), keyword()) :: result()

Creates (and by default starts) an instance from an image - the equivalent of incus launch.

image is either an alias string resolved against the public image server (https://images.linuxcontainers.org), or a full source map which is passed through untouched.

Options

  • :server / :protocol - override the image remote (protocol defaults to "simplestreams")
  • :config, :devices, :profiles, :type ("container" or "virtual-machine"), :ephemeral, :description - instance fields
  • :start - start after creation (default true)
  • :wait / :timeout - operation waiting, as everywhere else

Examples

ExIncus.Instances.launch(client, "web1", "debian/12")

# From a local image alias instead of the public server:
ExIncus.Instances.launch(client, "web2", %{"type" => "image", "alias" => "my-golden"},
  config: %{"limits.cpu" => "2"})

list(client, opts \\ [])

@spec list(
  ExIncus.Client.t(),
  keyword()
) :: result()

Lists instances.

Returns full objects by default. Options: recursion: (0 = URLs only, 1 = objects, 2 = objects incl. runtime state), filter: (server-side filter expression, e.g. "status eq Running"), project:.

list_files(client, name, dir_path, opts \\ [])

@spec list_files(ExIncus.Client.t(), name(), String.t(), keyword()) ::
  {:ok, [String.t()]} | {:error, ExIncus.Error.t()}

Lists the entries of a directory inside the instance.

logs(client, name, opts \\ [])

@spec logs(ExIncus.Client.t(), name(), keyword()) :: result()

Lists available log files (as API URLs).

mkdir(client, name, dir_path, opts \\ [])

@spec mkdir(ExIncus.Client.t(), name(), String.t(), keyword()) :: result()

Creates a directory inside the instance. Options: :uid, :gid, :mode.

patch(client, name, params, opts \\ [])

@spec patch(ExIncus.Client.t(), name(), map(), keyword()) :: result()

Partially updates the instance (PATCH). Supplied "config" and "devices" entries are merged into the existing ones; entries you don't mention are kept.

pull_file(client, name, file_path, opts \\ [])

@spec pull_file(ExIncus.Client.t(), name(), String.t(), keyword()) ::
  {:ok, binary()} | {:error, ExIncus.Error.t()}

Reads a file from the instance and returns its content as a binary.

push_file(client, name, file_path, content, opts \\ [])

@spec push_file(ExIncus.Client.t(), name(), String.t(), iodata(), keyword()) ::
  result()

Writes a file into the instance, creating or overwriting it.

ExIncus.Instances.push_file(client, "web1", "/etc/resolv.conf",
  "nameserver 10.140.100.1\n", uid: 0, gid: 0, mode: 0o644)

Options

  • :uid / :gid - numeric owner (defaults to root on the server side)
  • :mode - file mode, as an integer (0o644) or octal string ("0644")
  • :append - append to the file instead of overwriting

remove_device(client, name, device_name, opts \\ [])

@spec remove_device(ExIncus.Client.t(), name(), String.t(), keyword()) :: result()

Removes a device. PATCH can only merge, so this reads the instance and writes back the full configuration without the device (PUT).

rename(client, name, new_name, opts \\ [])

@spec rename(ExIncus.Client.t(), name(), name(), keyword()) :: result()

Renames a stopped instance.

restart(client, name, opts \\ [])

@spec restart(ExIncus.Client.t(), name(), keyword()) :: result()

Restarts the instance. See set_state/4 for options.

set_device(client, name, device_name, device, opts \\ [])

@spec set_device(ExIncus.Client.t(), name(), String.t(), map(), keyword()) :: result()

Adds or replaces a single device, leaving other devices untouched.

ExIncus.Instances.set_device(client, "web1", "eth0", %{
  "type" => "nic",
  "network" => "incusbr0",
  "ipv4.address" => "10.140.100.10"
})

Note the device entry is replaced as a whole, so include every key the device needs, not just the changed one.

set_state(client, name, action, opts \\ [])

@spec set_state(ExIncus.Client.t(), name(), atom(), keyword()) :: result()

Changes the runtime state. action is one of :start, :stop, :restart, :freeze, :unfreeze.

Options

  • :force - kill instead of graceful shutdown (stop/restart)
  • :action_timeout - seconds the server allows the action to take before giving up (the timeout field of the API request)
  • :stateful - store/restore runtime state (start/stop)
  • :wait / :timeout - operation waiting

start(client, name, opts \\ [])

@spec start(ExIncus.Client.t(), name(), keyword()) :: result()

Starts the instance. See set_state/4 for options.

state(client, name, opts \\ [])

@spec state(ExIncus.Client.t(), name(), keyword()) :: result()

Fetches the runtime state: status, network interfaces and addresses, resource usage, PIDs.

stop(client, name, opts \\ [])

@spec stop(ExIncus.Client.t(), name(), keyword()) :: result()

Stops the instance (gracefully; pass force: true to kill). See set_state/4.

unfreeze(client, name, opts \\ [])

@spec unfreeze(ExIncus.Client.t(), name(), keyword()) :: result()

Unfreezes the instance.

update(client, name, params, opts \\ [])

@spec update(ExIncus.Client.t(), name(), map(), keyword()) :: result()

Replaces the instance configuration (PUT). params must be a full InstancePut map; for partial changes prefer patch/4 or update_config/4.

update_config(client, name, config, opts \\ [])

@spec update_config(ExIncus.Client.t(), name(), map(), keyword()) :: result()

Merges the given keys into the instance config, leaving other keys untouched. Values are stringified (the API only accepts strings).

ExIncus.Instances.update_config(client, "web1", %{"boot.autostart" => true})