Current section

Files

Jump to
lxd lib endpoints container.ex
Raw

lib/endpoints/container.ex

defmodule LXD.Container do
alias LXD.Client
alias LXD.Utils
defp url(container_name \\ "", opts \\ []) do
exec = if(opts[:exec], do: "/exec", else: "")
state = if(opts[:state], do: "/state", else: "")
metadata = if(opts[:metadata], do: "/metadata", else: "")
["/containers", container_name, exec, state, metadata]
|> Path.join
end
@doc """
List all containers
"""
def all(opts \\ []) do
url()
|> Client.get(opts)
|> case do
{:ok, containers} ->
{:ok, containers |> Enum.map(&Path.basename/1)}
other ->
other
end
end
@doc """
Create a new container
`configs` is a map that define how to create the container
See official documention for more details [here](https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1)
"""
def create(configs, opts \\ []) do
url()
|> Client.post(configs, opts)
end
@doc """
Container information
"""
def info(container_name, opts \\ []) do
url(container_name)
|> Client.get(opts)
end
@doc """
Replace container configuration
"""
def replace(container_name, configs, opts \\ []) do
url(container_name)
|> Client.put(configs, opts)
end
@doc """
Update container configuration
"""
def update(container_name, configs, opts \\ []) do
url(container_name)
|> Client.patch(configs, opts)
end
@doc """
Rename the container
"""
def rename(container_name, new_name, opts \\ []) do
url(container_name)
|> Client.post(%{"name" => new_name}, opts)
end
@doc """
Remove the container
"""
def remove(container_name, opts \\ []) do
url(container_name)
|> Client.delete(opts)
end
@doc """
Run a remote command
- `command`: must be a list splitted by space (see official documentation)
- `envs`: map of environement variables to set
Record output is set to true
Return `{:ok, return_code, stdout, stderr}` if success
Return `{:error, reason}` if error
If stdout or stderr can't be read, `:error` is set instead
Does not support websocket
See official documention for more details [here](https://github.com/lxc/lxd/blob/master/doc/rest-api.md#10containersnameexec)
"""
def exec(container_name, command, envs \\ %{}, opts \\ []) do
configs = %{
"command" => command,
"environment" => envs,
"record-output" => true
}
url(container_name, exec: true)
|> Client.post(configs, opts)
|> case do
{:ok, %{"output" => %{"1" => stdout, "2" => stderr}, "return" => return}} ->
get_result = fn log_name ->
case LXD.Container.Log.get(container_name, log_name |> Path.basename) do
{:ok, body} when is_binary(body) ->
body
_ ->
:error
end
end
{:ok, return, get_result.(stdout), get_result.(stderr)}
{:ok, _body} ->
{:error, "Cannot find output logs"}
{:error, reason} ->
{:error, reason}
end
end
@doc """
State of the container
"""
def state(container_name, opts \\ []) do
url(container_name, state: true)
|> Client.get(opts)
end
@doc """
Status of the container as a string
"""
def status(name) do
case state(name) do
{:ok, %{"status" => value}} ->
{:ok, value}
other ->
other
end
end
@doc """
Stop the container
"""
def stop(container_name, opts \\ []) do
state_change("stop", container_name, opts)
end
def start(container_name, opts \\ []) do
state_change("start", container_name, opts)
end
def restart(container_name, opts \\ []) do
state_change("restart", container_name, opts)
end
def freeze(container_name, opts \\ []) do
state_change("freeze", container_name, opts)
end
def unfreeze(container_name, opts \\ []) do
state_change("unfreeze", container_name, opts)
end
defp state_change(action, container_name, opts) do
force = Utils.arg(opts, :force, false)
timeout = Utils.arg(opts, :timeout, 0)
stateful = Utils.arg(opts, :stateful, false)
input = %{
"action" => action,
"timeout" => timeout,
"force" => force,
"stateful" => stateful
}
url(container_name, state: true)
|> Client.put(input, opts)
|> case do
{:ok, %{"status_code" => 200}} ->
:ok
{:ok, %{"err" => error}} ->
{:error, error}
other ->
other
end
end
def metadata(container_name, opts \\ []) do
url(container_name, metadata: true)
|> Client.get(opts)
end
end
defmodule LXD.Container.Template do
alias LXD.Client
defp url(container_name) do
["/containers", container_name, "/metadata/templates"]
|> Path.join
end
def all(container_name, opts \\ []) do
url(container_name)
|> Client.get(opts)
end
def get(container_name, template_name, opts \\ []) do
url(container_name)
|> Client.get(opts, [], params: [{"path", template_name}])
end
end
defmodule LXD.Container.Log do
alias LXD.Client
defp url(container_name, log_name \\ []) do
["/containers", container_name, "/logs", log_name]
|> Path.join
end
def all(container_name, opts \\ []) do
url(container_name)
|> Client.get(opts)
|> case do
{:ok, data} ->
{:ok, data |> Enum.map(&Path.basename/1)}
others ->
others
end
end
def get(container_name, log_name, opts \\ []) do
url(container_name, log_name)
|> Client.get(opts)
end
end
defmodule LXD.Container.File do
alias LXD.Utils
alias LXD.Client
defp url(container_name) do
["/containers", container_name, "/files"]
|> Path.join
end
def get(container_name, path_in_container, opts \\ []) do
url(container_name)
|> Client.get(opts, [], params: [{"path", path_in_container}])
end
def get_and_write(container_name, path_in_container, target_path, opts \\ []) do
url(container_name)
|> Client.get(opts, [], params: [{"path", path_in_container}])
|> case do
{:ok, ls} when is_list(ls) ->
case File.mkdir(target_path) do
:ok ->
ls |> Enum.each(fn one ->
get_and_write(
container_name,
[path_in_container, one] |> Path.join,
[target_path, one] |> Path.join,
opts
)
end)
{:error, posix} ->
{:error, "Cannot create directory #{target_path} (reason: #{:file.format_error(posix)})"}
end
{:ok, file_content} when is_binary(file_content) ->
case File.write(target_path, file_content) do
:ok ->
:ok
{:error, posix} ->
{:error, "Cannot write in file #{target_path} (reason: #{:file.format_error(posix)})"}
end
other ->
other
end
end
defp put(container_name, path_in_container, type, content, opts) do
append = Utils.arg(opts, :append, false)
headers = [{"x-lxd-type", type}]
headers = [{"x-lxd-write", if(append, do: "append", else: "overwrite")} | headers ]
url(container_name)
|> Client.post(content, opts, headers, params: [{"path", path_in_container}])
|> case do
{:ok, _} ->
:ok
others ->
others
end
end
def put_file(container_name, path_in_container, content, opts \\ []) do
put(container_name, path_in_container, "file", content, opts)
end
def create_dir(container_name, path_in_container, opts \\ []) do
put(container_name, path_in_container, "directory", "", opts)
end
def read_and_put(container_name, path_file, path_in_container, opts \\ []) do
case File.exists?(path_file) do
true ->
case File.dir?(path_file) do
true ->
create_dir(container_name, path_in_container, opts)
case File.ls(path_file) do
{:ok, ls} ->
ls |> Enum.reduce(:ok, fn(file, acc) ->
case acc do
:ok ->
read_and_put(
container_name,
[path_file, file] |> Path.join,
[path_in_container, file] |> Path.join,
opts
)
other ->
other
end
end)
{:error, reason} ->
{:error, "Failed to list files in #{path_file} (reason: #{reason})"}
end
false ->
case File.read(path_file) do
{:ok, binary} ->
put_file(container_name, path_in_container, binary)
{:error, posix} ->
{:error, "Failed to read file #{path_file} (#{:file.format_error(posix)})"}
end
end
false ->
{:error, "File #{path_file} doesn't exist"}
end
end
def remove(container_name, path_in_container, opts \\ []) do
url(container_name)
|> Client.delete(opts, [], params: [{"path", path_in_container}])
end
end
defmodule LXD.Container.Snapshot do
alias LXD.Client
defp url(container_name, snapshot_name \\ "") do
["/containers/", container_name, "/snapshots", snapshot_name]
|> Path.join
end
def all(container_name, opts \\ []) do
url(container_name)
|> Client.get(opts)
end
def create(container_name, snapshot_name, stateful \\ true, opts \\ []) do
input = %{
"name" => snapshot_name,
"stateful" => stateful
}
url(container_name)
|> Client.post(input, opts)
end
def get(container_name, snapshot_name, opts \\ []) do
url(container_name, snapshot_name)
|> Client.get(opts)
end
def rename(container_name, snapshot_name, new_name, opts \\ []) do
url(container_name, snapshot_name)
|> Client.post(%{ "name" => new_name }, opts)
end
def remove(container_name, snapshot_name, opts \\ []) do
url(container_name, snapshot_name)
|> Client.delete(opts)
end
end