module Hyperkit::Client::Containers

Methods for the containers API

@see Hyperkit::Client @see github.com/lxc/lxd/blob/master/doc/rest-api.md

Constants

REMOTE_IMAGE_ARGS

@!endgroup

Public Instance Methods

container(name) click to toggle source

Get information on a container

@param name [String] Container name @return [Sawyer::Resource] Container information

@example Get information about a container

Hyperkit.container("test-container") #=> {
  :architecture => "x86_64",
  :config => {
    :"volatile.base_image" => "097e75d6f7419d3a5e204d8125582f2d7bdd4ee4c35bd324513321c645f0c415",
    :"volatile.eth0.hwaddr" => "00:16:3e:24:5d:7a",
    :"volatile.eth0.name" => "eth0",
    :"volatile.last_state.idmap" =>
      "[{\"Isuid\":true,\"Isgid\":false,\"Hostid\":165536,\"Nsid\":0,\"Maprange\":65536},{\"Isuid\":false,\"Isgid\":true,\"Hostid\":165536,\"Nsid\":0,\"Maprange\":65536}]"
  },
  :created_at => 2016-03-18 20:55:26 UTC,
  :devices => {
    :root => {:path => "/", :type => "disk"}
  },
  :ephemeral => false,
  :expanded_config => {
    :"volatile.base_image" => "097e75d6f7419d3a5e204d8125582f2d7bdd4ee4c35bd324513321c645f0c415",
    :"volatile.eth0.hwaddr" => "00:16:3e:24:5d:7a",
    :"volatile.eth0.name" => "eth0",
    :"volatile.last_state.idmap" =>
      "[{\"Isuid\":true,\"Isgid\":false,\"Hostid\":165536,\"Nsid\":0,\"Maprange\":65536},{\"Isuid\":false,\"Isgid\":true,\"Hostid\":165536,\"Nsid\":0,\"Maprange\":65536}]"
  },
  :expanded_devices => {
    :eth0 => { :nictype => "bridged", :parent => "lxcbr0", :type => "nic"},
    :root => { :path => "/", :type => "disk"}
  },
  :name => "test-container",
  :profiles => ["default"],
  :stateful => false,
  :status => "Stopped",
  :status_code => 102
}
# File lib/hyperkit/client/containers.rb, line 64
def container(name)
  get(container_path(name)).metadata
end
container_state(name) click to toggle source

Retrieve the current state of a container

@param name [String] Container name @return [Sawyer::Resource] Container state

@example Get container state

Hyperkit.container_state("test-container") #=> {
}
# File lib/hyperkit/client/containers.rb, line 362
def container_state(name)
  get(container_state_path(name)).metadata
end
containers() click to toggle source

List of containers on the server (public or private)

@return [Array<String>] An array of container names

@example Get list of containers

Hyperkit.containers #=> ["container1", "container2", "container3"]
# File lib/hyperkit/client/containers.rb, line 22
def containers
  response = get(containers_path)
  response.metadata.map { |path| path.split('/').last }
end
copy_container(source_name, target_name, options={}) click to toggle source

Create a copy of an existing local container.

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param source_name [String] Source container name @param target_name [String] Target container name @param options [Hash] Additional data to be passed @option options [String] :architecture Architecture of the container (e.g. x86_64). By default, this will be obtained from the image metadata @option options [Hash] :config Container configuration @option options [Boolean] :ephemeral Whether to make the container ephemeral (i.e. delete it when it is stopped; default: false) @option options [Array] :profiles List of profiles to be applied to the container (default: []) @option options [Boolean] :sync If false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Copy container

Hyperkit.copy_container("existing", "new")

@example Copy container and override its configuration.

# Set the MAC address of the container's eth0 device
Hyperkit.copy_container("existing", "new", config: {
    "volatile.eth0.hwaddr" => "aa:bb:cc:dd:ee:ff"
  }
)

@example Copy container and apply profiles to it

Hyperkit.copy_container("existing", "new", profiles: ["migratable", "unconfined"])

@example Create container from a publicly-accessible remote image

Hyperkit.create_container("test-container",
  server: "https://images.linuxcontainers.org:8443",
  alias: "ubuntu/xenial/amd64")
# File lib/hyperkit/client/containers.rb, line 190
def copy_container(source_name, target_name, options={})

  opts = {
    source: {
      type: "copy",
      source: source_name
    }
  }.merge(extract_container_options(target_name, options))

  response = post(containers_path, opts).metadata
  handle_async(response, options[:sync])

end
create_container(name, options={}) click to toggle source

Create a container from an image (local or remote). The container will be created in the Stopped state.

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param name [String] Container name @param options [Hash] Additional data to be passed @option options [String] :alias Alias of the source image. Either :alias, :fingerprint, :properties, or empty: true must be specified. @option options [String] :architecture Architecture of the container (e.g. x86_64). By default, this will be obtained from the image metadata @option options [String] :certificate PEM certificate to use to authenticate with the remote server. If not specified, and the source image is private, the target LXD server's certificate is used for authentication. This option is valid only when transferring an image from a remote server using the :server option. @option options [Hash] :config Container configuration @option options [Boolean] :ephemeral Whether to make the container ephemeral (i.e. delete it when it is stopped; default: false) @option options [Boolean] :empty Whether to make an empty container (i.e. not from an image). Specifying true will cause LXD to create a container with no rootfs. That is, /var/lib/lxd/<container-name> will simply be an empty directly. One can then create a rootfs directory within this directory and populate it manually. This is useful when migrating LXC containers to LXD. @option options [String] :fingerprint SHA-256 fingerprint of the source image. This can be a prefix of a fingerprint, as long as it is unambiguous. Either :alias, :fingerprint, :properties, or empty: true must be specified. @option options [Array] :profiles List of profiles to be applied to the container (default: []) @option options [String] :properties Properties of the source image. Either :alias, :fingerprint, :properties, or empty: true must be specified. @option options [String] :protocol Protocol to use in transferring the image (lxd or simplestreams; defaults to lxd). This option is valid only when transferring an image from a remote server using the :server option. @option options [String] :secret Secret to use to retrieve the image. This option is valid only when transferring an image from a remote server using the :server option. @option options [String] :server URL of remote server from which to obtain image. By default, the image will be obtained from the client's api_endpoint. @option options [Boolean] :sync If false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Create container from image specified by alias

Hyperkit.create_container("test-container", alias: "ubuntu/xenial/amd64")

@example Create container from image specified by fingerprint

Hyperkit.create_container("test-container",
  fingerprint: "097e75d6f7419d3a5e204d8125582f2d7bdd4ee4c35bd324513321c645f0c415")

@example Create container from image specified by fingerprint prefix

Hyperkit.create_container("test-container", fingerprint: "097")

@example Create container based on most recent match of image properties

Hyperkit.create_container("test-container",
  properties: { os: "ubuntu", release: "14.04", architecture: "x86_64" }

@example Create an empty container

Hyperkit.create_container("test-container", empty: true)

@example Create container with custom configuration.

# Set the MAC address of the container's eth0 device
Hyperkit.create_container("test-container",
  alias: "ubuntu/xenial/amd64",
  config: {
    "volatile.eth0.hwaddr" => "aa:bb:cc:dd:ee:ff"
  }
)

@example Create container and apply profiles to it

Hyperkit.create_container("test-container",
  alias: "ubuntu/xenial/amd64",
  profiles: ["migratable", "unconfined"]
)

@example Create container from a publicly-accessible remote image

Hyperkit.create_container("test-container",
  server: "https://images.linuxcontainers.org:8443",
  alias: "ubuntu/xenial/amd64")

@example Create container from a private remote image (authenticated by a secret)

Hyperkit.create_container("test-container",
  server: "https://private.example.com:8443",
  alias: "ubuntu/xenial/amd64",
  secret: "shhhhh")
# File lib/hyperkit/client/containers.rb, line 137
def create_container(name, options={})

  source = container_source_attribute(options)
  opts = options.except(:sync)

  if ! opts[:empty] && source.empty?
    raise Hyperkit::ImageIdentifierRequired.new("Specify source image by alias, fingerprint, or properties, or create an empty container with 'empty: true'")
  end

  if opts[:empty]
    opts = empty_container_options(name, opts)
  elsif options[:server]
    opts = remote_image_container_options(name, source, opts)
  else
    opts = local_image_container_options(name, source, opts)
  end

  response = post(containers_path, opts).metadata
  handle_async(response, options[:sync])
end
create_snapshot(container, snapshot, options={}) click to toggle source

Create a snapshot of a container

If stateful: true is passed when creating a snapshot of a running container, the container's runtime state will be stored in the snapshot. Note that CRIU must be installed on the server to create a stateful snapshot, or LXD will return a 500 error. On Ubuntu, you can install it with sudo apt-get install criu.

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param container [String] Container name @param snapshot [String] Snapshot name @param options [Hash] Additional data to be passed @option options [Boolean] :stateful Whether to save runtime state for a running container (requires CRIU on the server; default: false</false>) @option options [Boolean] :sync If <code>false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Create stateless snapshot for container 'test'

Hyperkit.create_snapshot("test", "snap1")

@example Create snapshot and save runtime state for running container 'test'

Hyperkit.create_snapshot("test", "snap1", stateful: true)
# File lib/hyperkit/client/containers.rb, line 756
def create_snapshot(container, snapshot, options={})
  opts = options.slice(:stateful)
  opts[:name] = snapshot
  response = post(snapshots_path(container), opts).metadata
  handle_async(response, options[:sync])
end
delete_container(name, options={}) click to toggle source

Delete a container. Throws an error if the container is running.

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param name [String] Container name @param options [Hash] Additional data to be passed @option options [Boolean] :sync If false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Delete container “test”

Hyperkit.delete_container("test")
# File lib/hyperkit/client/containers.rb, line 345
def delete_container(name, options={})
  response = delete(container_path(name)).metadata
  handle_async(response, options[:sync])
end
delete_log(container, log) click to toggle source

Delete a container's log

@param container [String] Container name @param log [String] Filename of log to delete

@example Delete log “lxc.log” for container “test-container”

Hyperkit.delete_log("test-container", "lxc.log")
# File lib/hyperkit/client/containers.rb, line 991
def delete_log(container, log)
  delete(log_path(container, log))
end
delete_snapshot(container, snapshot, options={}) click to toggle source

Delete a snapshot

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param container [String] Container name @param snapshot [String] Snapshot name @param options [Hash] Additional data to be passed @option options [Boolean] :sync If false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Delete snapshot “snap” from container “test”

Hyperkit.delete_snapshot("test", "snap")
# File lib/hyperkit/client/containers.rb, line 776
def delete_snapshot(container, snapshot, options={})
  response = delete(snapshot_path(container, snapshot)).metadata
  handle_async(response, options[:sync])
end
execute_command(container, command, options={}) click to toggle source

Execute a command in a container

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param container [String] Container name @param command [Array|String] Command to execute @param options [Hash] Additional data to be passed @option options [Hash] :environment Environment variables to set prior to command execution @option options [Boolean] :sync If false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @option options [Boolean] :wait_for_websocket If true block and wait for a websocket connection to start. @option options [Boolean] :interactive If truea single websocket is returned and is mapped to a pts device for stdin, stdout and stderr of the execed process. If false, three pipes will be setup, one for each of stdin, stdout and stderr. @option options [Boolean] :record_output If true, captures the output of stdout and stderr. @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Run a command (passed as a string) in container “test-container”

Hyperkit.execute_command("test-container", "echo 'hello world'")

@example Run a command (passed as an array) in container “test-container”

Hyperkit.execute_command("test-container",
  ["bash", "-c", "echo 'hello world' > /tmp/test.txt"]
)

@example Run a command and pass environment variables

Hyperkit.execute_command("test-container",
  "/bin/sh -c 'echo \"$MYVAR\" $MYVAR2 > /tmp/test.txt'",
  environment: {
    MYVAR: "hello world",
    MYVAR2: 42
  }
)
# File lib/hyperkit/client/containers.rb, line 308
def execute_command(container, command, options={})

  opts = options.slice(:environment)
  command = Shellwords.shellsplit(command) if command.is_a?(String)

  opts[:environment] = stringify_hash(opts[:environment]) if opts[:environment]

  response = post(File.join(container_path(container), "exec"), {
    command: command,
    environment: opts[:environment] || {},
    "wait-for-websocket" => options[:wait_for_websocket] || false,
    interactive: options[:interactive] || false,
    "record-output" => options[:record_output] || false
  }).metadata

  handle_async(response, options[:sync])

end
Also aliased as: run_command
freeze_container(name, options={}) click to toggle source

Freeze (suspend) a running container

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param name [String] Container name @param options [Hash] Additional data to be passed @option options [Boolean] :sync If false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @option options [Fixnum] :timeout Time after which the operation is considered to have failed (default: no timeout) @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Suspend container

Hyperkit.freeze_container("test")

@example Suspend container with timeout

Hyperkit.freeze_container("test", timeout: 30)
# File lib/hyperkit/client/containers.rb, line 465
def freeze_container(name, options={})
  opts = options.slice(:timeout)
  response = put(container_state_path(name), opts.merge(action: "freeze")).metadata
  handle_async(response, options[:sync])
end
init_migration(container, snapshot=nil) click to toggle source

Prepare to migrate a container or snapshot. Generates source data to be passed to {#migrate}.

Note that CRIU must be installed on the server to migrate a running container, or LXD will return a 500 error. On Ubuntu, you can install it with sudo apt-get install criu.

@param name [String] Container name @return [Sawyer::Resource] Source data to be passed to {#migrate}

@example Retrieve migration source data for container “test”

Hyperkit.init_migration("test") #=> {
  :architecture => "x86_64",
  :config => {
    :"volatile.base_image" => "b41f6b96f103335eafbf38ba65488eda66b05b08b590130e473803631d66ff38",
    :"volatile.eth0.hwaddr" => "00:16:3e:e9:d5:5c",
    :"volatile.last_state.idmap" =>
      "[{\"Isuid\":true,\"Isgid\":false,\"Hostid\":231072,\"Nsid\":0,\"Maprange\":65536},{\"Isuid\":false,\"Isgid\":true,\"Hostid\":231072,\"Nsid\":0,\"Maprange\":65536}]"
  },
  :profiles => ["default"],
  :websocket => {
    :url => "https://192.168.103.101:8443/1.0/operations/a30aca8e-8ff3-4437-b1da-bb28b43ee876",
    :secrets => {
      :control => "a6f8d21ebfe9ec76bf56585c98fd6d700fd43edee513ce61e48e1abeef479106",
      :criu => "c8601ec0d07f97f206835dde5783640c08640e9b27e45624d8555546b0cca327",
      :fs => "ddf9d064331b9f3728d098873a8a89a7742b8e656f2cd0815f0aee4777ff2b54"
    }
  },
  :certificate => "source server SSL certificate"
}

@example Retrieve migration source data for snapshot “snap” of container “test”

Hyperkit.init_migration("test", "snap") #=> {
  :architecture => "x86_64",
  :config => {
    :"volatile.apply_template" => "create",
    :"volatile.base_image" => "b41f6b96f103335eafbf38ba65488eda66b05b08b590130e473803631d66ff38",
    :"volatile.eth0.hwaddr" => "00:16:3e:e9:d5:5c",
    :"volatile.last_state.idmap" =>
      "[{\"Isuid\":true,\"Isgid\":false,\"Hostid\":231072,\"Nsid\":0,\"Maprange\":65536},{\"Isuid\":false,\"Isgid\":true,\"Hostid\":231072,\"Nsid\":0,\"Maprange\":65536}]"
  },
  :profiles => ["default"],
  :websocket => {
    :url => "https://192.168.103.101:8443/1.0/operations/a30aca8e-8ff3-4437-b1da-bb28b43ee876",
    :secrets => {
      :control => "a6f8d21ebfe9ec76bf56585c98fd6d700fd43edee513ce61e48e1abeef479106",
      :criu => "c8601ec0d07f97f206835dde5783640c08640e9b27e45624d8555546b0cca327",
      :fs => "ddf9d064331b9f3728d098873a8a89a7742b8e656f2cd0815f0aee4777ff2b54"
    }
  },
  :certificate => "source server SSL certificate"
}
# File lib/hyperkit/client/containers.rb, line 552
def init_migration(container, snapshot=nil)

  if snapshot
    url = snapshot_path(container, snapshot)
    source = snapshot(container, snapshot)
  else
    url = container_path(container)
    source = container(container)
  end

  response = post(url, { migration: true })
  agent = response.agent

  source_data = {
    architecture: source.architecture,
    config: source.config.to_hash,
    profiles: source.profiles,
    websocket: {
      url: File.join(api_endpoint, response.operation),
      secrets: response.metadata.metadata.to_hash,
    },
    certificate: get("/1.0").metadata.environment.certificate,
    snapshot: ! snapshot.nil?
  }

  Sawyer::Resource.new(response.agent, source_data)
end
log(container, log) click to toggle source

Retrieve the contents of a log for a container

@param container [String] Container name @param log [String] Log filename @return [String] The contents of the log

@example Get log “lxc.log” for container “test-container”

Hyperkit.log("test-container", "lxc.log")
# File lib/hyperkit/client/containers.rb, line 980
def log(container, log)
  get(log_path(container, log))
end
logs(container) click to toggle source

Retrieve a list of logs for a container

@param container [String] Container name @return [Array<String>] An array of log filenames

@example Get list of logs for container “test-container”

Hyperkit.logs("test-container")
# File lib/hyperkit/client/containers.rb, line 967
def logs(container)
  response = get(logs_path(container))
  response.metadata.map { |path| path.sub(logs_path(container) + '/', '') }
end
migrate(source, dest_name, options={}) click to toggle source

Migrate a remote container or snapshot to the server

Note that CRIU must be installed on the server to migrate a running container, or LXD will return a 500 error. On Ubuntu, you can install it with sudo apt-get install criu.

Also note that, unless overridden with the profiles parameter, if the source container has profiles applied to it that do not exist on the target LXD instance, this method will throw an exception.

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param source [Sawyer::Resource] Source data retrieve from the remote server with {#init_migration} @param dest_name [String] Name of the new container @param options [Hash] Additional data to be passed @option options [String] :architecture Architecture of the container (e.g. x86_64). By default, this will be obtained from the image metadata @option options [String] :certificate PEM certificate of the source server. If not specified, defaults to the certificate returned by the source server in the source parameter. @option options [Hash] :config Container configuration @option options [Boolean] :ephemeral Whether to make the container ephemeral (i.e. delete it when it is stopped; default: false) @option options [Boolean] :move Whether the container is being moved (true) or copied (false). Note that this does not actually delete the container from the remote LXD instance. Specifying move: true prevents regenerating volatile data (such as a container's MAC addresses), while move: false will regenerate all of this data. Defaults to false (a copy) @option options [Array] :profiles List of profiles to be applied to the container (default: []) @option options [Boolean] :sync If false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Migrate container from remote instance

remote_lxd = Hyperkit::Client.new(api_endpoint: "remote.example.com")
source_data = remote_lxd.init_migration("remote-container")
Hyperkit.migrate(source_data, "new-container")

@example Migrate container and do not regenerate volatile data (e.g. MAC addresses)

remote_lxd = Hyperkit::Client.new(api_endpoint: "remote.example.com")
source_data = remote_lxd.init_migration("remote-container")
Hyperkit.migrate(source_data, "new-container", move: true)

@example Migrate container and override its profiles

remote_lxd = Hyperkit::Client.new(api_endpoint: "remote.example.com")
source_data = remote_lxd.init_migration("remote-container")
Hyperkit.migrate(source_data, "new-container", profiles: %w[test-profile1 test-profile2])

@example Migrate a snapshot

remote_lxd = Hyperkit::Client.new(api_endpoint: "remote.example.com")
source_data = remote_lxd.init_migration("remote-container", "remote-snapshot")
Hyperkit.migrate(source_data, "new-container", profiles: %w[test-profile1 test-profile2])
# File lib/hyperkit/client/containers.rb, line 623
def migrate(source, dest_name, options={})

  opts = {
    name: dest_name,
    architecture: options[:architecture] || source.architecture,
    source: {
      type: "migration",
      mode: "pull",
      operation: source.websocket.url,
      certificate: options[:certificate] || source.certificate,
      secrets: source.websocket.secrets.to_hash
    }
  }

  if ! source.snapshot
    opts["base-image"] = source.config["volatile.base_image"]
    opts[:config] = options[:config] || source.config.to_hash

    # If we're only copying the container, and configuration was explicitly
    # overridden, then remove the volatile entries
    if ! options[:move] && ! options.has_key?(:config)
      opts[:config].delete_if { |k,v| k.to_s.start_with?("volatile") }
    end

  else
    opts[:config] = options[:config] || {}
  end

  if options.has_key?(:profiles)
    opts[:profiles] = options[:profiles]
  else

    dest_profiles = profiles()

    if (source.profiles - dest_profiles).empty?
      opts[:profiles] = source.profiles
    else
      raise Hyperkit::MissingProfiles.new("Not all profiles applied to source container exist on the target LXD instance")
    end

  end

  if options.has_key?(:ephemeral)
    opts[:ephemeral] = options[:ephemeral]
  else
    opts[:ephemeral] = !! source.ephemeral
  end

  response = post(containers_path, opts).metadata
  handle_async(response, options[:sync])
end
pause_container(name, options={})
Alias for: freeze_container
pull_file(container, source_file, dest) click to toggle source

Copy a file from a container to the local system. The file will be written with the same permissions assigned to it in the container.

@param container [String] Container name @param source_file [String] Full path to a file within the container @param dest [String, IO] Full path of desired output file (will be created/overwritten), or an IO object to write to @return [String] Full path to the local output file

@example Copy /etc/passwd in container “test” to the local file /tmp/passwd

Hyperkit.pull_file("test", "/etc/passwd", "/tmp/passwd") #=> "/tmp/passwd"

@example Copy /etc/passwd in container “test” to a StringIO object

Hyperkit.pull_file("test", "/etc/passwd", StringIO.new) #=> <StringIO:0x007fd196061a70>
# File lib/hyperkit/client/containers.rb, line 847
def pull_file(container, source_file, dest)
  contents = get(file_path(container, source_file), url_encode: false)
  headers = last_response.headers

  if dest.respond_to? :write
    dest.write(contents)
  else
    File.open(dest, "wb") do |f|
      f.write(contents)
    end

    if headers["x-lxd-mode"]
      File.chmod(headers["x-lxd-mode"].to_i(8), dest)
    end
  end

  dest

end
push_file(source, container, dest_file, options={}) click to toggle source

Copy a file from the local system to container

@param container [String] Container name @param source_file [String] Full path to a file within the container @param dest [String, IO] Full path of desired output file (will be created/overwritten), or an IO object to write to @param options [Hash] Additional data to be passed @option options [Fixnum] :uid Owner to assign to the file @option options [Fixnum] :gid Group to assign to the file @option options [Fixnum] :mode File permissions (in octal) to assign to the file @return [Sawyer::Resource]

@example Copy /tmp/test.txt from the local system to /etc/passwd in the container

Hyperkit.push_file("/tmp/test.txt", "test-container", "/etc/passwd")

@example Write the contents of a StringIO object to /etc/passwd in the container

Hyperkit.push_file(StringIO.new("test string"), "test-container", "/etc/passwd")

@example Assign uid, gid, and mode to a file:

Hyperkit.push_file("/tmp/test.txt",
  "test-container",
  "/etc/passwd",
  uid: 1000,
  gid: 1000,
  mode: 0644
)
# File lib/hyperkit/client/containers.rb, line 944
def push_file(source, container, dest_file, options={})

  write_file(container, dest_file, options) do |f|
    if source.respond_to? :read
      f.write source.read
    else
      f.write File.read(source)
    end
  end

end
read_file(container, file) click to toggle source

Read the contents of a file in a container

@param container [String] Container name @param file [String] Full path to a file within the container @return [String] The contents of the file

@example Read the file /etc/hostname in container “test”

Hyperkit.read_file("test", "/etc/hostname") #=> "test-container.example.com"
# File lib/hyperkit/client/containers.rb, line 830
def read_file(container, file)
  get(file_path(container, file), url_encode: false)
end
rename_container(old_name, new_name, options={}) click to toggle source

Rename a container

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param old_name [String] Existing container name @param new_name [String] New container name @param options [Hash] Additional data to be passed @option options [Boolean] :sync If false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Rename container “test” to “test2”

Hyperkit.rename_container("test", "test2")
# File lib/hyperkit/client/containers.rb, line 269
def rename_container(old_name, new_name, options={})
  response = post(container_path(old_name), { name: new_name }).metadata
  handle_async(response, options[:sync])
end
rename_snapshot(container, old_name, new_name, options={}) click to toggle source

Rename a snapshot

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param container [String] Container name @param old_name [String] Existing snapshot name @param new_name [String] New snapshot name @param options [Hash] Additional data to be passed @option options [Boolean] :sync If false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Rename snapshot “test/snap1” to “snap2”

Hyperkit.rename_snapshot("test", "snap1", "snap2")
# File lib/hyperkit/client/containers.rb, line 794
def rename_snapshot(container, old_name, new_name, options={})
  response = post(snapshot_path(container, old_name), { name: new_name }).metadata
  handle_async(response, options[:sync])
end
restart_container(name, options={}) click to toggle source

Restart a running container

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param name [String] Container name @param options [Hash] Additional data to be passed @option options [Boolean] :force Whether to force the operation by killing the container @option options [Boolean] :sync If false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @option options [Fixnum] :timeout Time after which the operation is considered to have failed (default: no timeout) @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Restart container

Hyperkit.restart_container("test")

@example Restart container forcefully

Hyperkit.restart_container("test", force: true)

@example Restart container with timeout

Hyperkit.restart_container("test", timeout: 30)
# File lib/hyperkit/client/containers.rb, line 444
def restart_container(name, options={})
  opts = options.slice(:force, :timeout)
  response = put(container_state_path(name), opts.merge(action: "restart")).metadata
  handle_async(response, options[:sync])
end
restore_snapshot(container, snapshot, options={}) click to toggle source

Restore a snapshot

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param container [String] Container name @param snapshot [String] Name of snapshot to restore @param options [Hash] Additional data to be passed @option options [Boolean] :sync If false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Restore container “test” back to snapshot “snap1”

Hyperkit.restore_snapshot("test", "snap1")
# File lib/hyperkit/client/containers.rb, line 811
def restore_snapshot(container, snapshot, options={})
  response = put(container_path(container), { restore: snapshot }).metadata
  handle_async(response, options[:sync])
end
Also aliased as: revert_to_snapshot
resume_container(name, options={})
Alias for: unfreeze_container
revert_to_snapshot(container, snapshot, options={})
Alias for: restore_snapshot
run_command(container, command, options={})
Alias for: execute_command
snapshot(container, snapshot) click to toggle source

Get information on a snapshot

@param container [String] Container name @param Snapshot [String] Snapshot name @return [Sawyer::Resource] Snapshot information

@example Get information about a snapshot

Hyperkit.snapshot("test-container", "test-snapshot") #=> {
  :architecture => "x86_64",
  :config => {
    :"volatile.apply_template" => "create",
    :"volatile.base_image" => "097e75d6f7419d3a5e204d8125582f2d7bdd4ee4c35bd324513321c645f0c415",
    :"volatile.eth0.hwaddr" => "00:16:3e:24:5d:7a",
    :"volatile.eth0.name" => "eth0",
    :"volatile.last_state.idmap" =>
      "[{\"Isuid\":true,\"Isgid\":false,\"Hostid\":165536,\"Nsid\":0,\"Maprange\":65536},{\"Isuid\":false,\"Isgid\":true,\"Hostid\":165536,\"Nsid\":0,\"Maprange\":65536}]"
  },
  :created_at => 2016-03-18 20:55:26 UTC,
  :devices => {
    :root => {:path => "/", :type => "disk"}
  },
  :ephemeral => false,
  :expanded_config => {
    :"volatile.apply_template" => "create",
    :"volatile.base_image" => "097e75d6f7419d3a5e204d8125582f2d7bdd4ee4c35bd324513321c645f0c415",
    :"volatile.eth0.hwaddr" => "00:16:3e:24:5d:7a",
    :"volatile.eth0.name" => "eth0",
    :"volatile.last_state.idmap" =>
      "[{\"Isuid\":true,\"Isgid\":false,\"Hostid\":165536,\"Nsid\":0,\"Maprange\":65536},{\"Isuid\":false,\"Isgid\":true,\"Hostid\":165536,\"Nsid\":0,\"Maprange\":65536}]"
  },
  :expanded_devices => {
    :eth0 => { :name => "eth0", :nictype => "bridged", :parent => "lxcbr0", :type => "nic"},
    :root => { :path => "/", :type => "disk"}
  },
  :name => "test-container/test-snapshot",
  :profiles => ["default"],
  :stateful => false
}
# File lib/hyperkit/client/containers.rb, line 729
def snapshot(container, snapshot)
  get(snapshot_path(container, snapshot)).metadata
end
snapshots(container) click to toggle source

List of snapshots for a container

@param container [String] Container name @return [Array<String>] An array of snapshot names

@example Get list of snapshots for container “test”

Hyperkit.snapshots("test") #=> ["snap1", "snap2", "snap3"]
# File lib/hyperkit/client/containers.rb, line 686
def snapshots(container)
  response = get snapshots_path(container)
  response.metadata.map { |path| path.split('/').last }
end
start_container(name, options={}) click to toggle source

Start a container

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param name [String] Container name @param options [Hash] Additional data to be passed @option options [Boolean] :stateful Whether to restore previously saved runtime state (default: false</false>) @option options [Boolean] :sync If <code>false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @option options [Fixnum] :timeout Time after which the operation is considered to have failed (default: no timeout) @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Start container

Hyperkit.start_container("test")

@example Start container and restore previously saved runtime state

# Stop the container and save its runtime state
Hyperkit.stop_container("test", stateful: true)

# Start the container and restore its runtime state
Hyperkit.start_container("test", stateful: true)

@example Start container with a timeout

Hyperkit.start_container("test", timeout: 30)
# File lib/hyperkit/client/containers.rb, line 389
def start_container(name, options={})
  opts = options.slice(:stateful, :timeout)
  response = put(container_state_path(name), opts.merge(action: "start")).metadata
  handle_async(response, options[:sync])
end
stop_container(name, options={}) click to toggle source

Stop a container

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param name [String] Container name @param options [Hash] Additional data to be passed @option options [Boolean] :force Whether to force the operation by killing the container @option options [Boolean] :stateful Whether to restore previously saved runtime state (default: false</false>) @option options [Boolean] :sync If <code>false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @option options [Fixnum] :timeout Time after which the operation is considered to have failed (default: no timeout) @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Stop container

Hyperkit.stop_container("test")

@example Stop container and save its runtime state

# Stop the container and save its runtime state
Hyperkit.stop_container("test", stateful: true)

# Start the container and restore its runtime state
Hyperkit.start_container("test", stateful: true)

@example Stop the container forcefully (i.e. kill it)

Hyperkit.stop_container("test", force: true)
# File lib/hyperkit/client/containers.rb, line 419
def stop_container(name, options={})
  opts = options.slice(:force, :stateful, :timeout)
  response = put(container_state_path(name), opts.merge(action: "stop")).metadata
  handle_async(response, options[:sync])
end
suspend_container(name, options={})
Alias for: freeze_container
unfreeze_container(name, options={}) click to toggle source

Unfreeze (resume) a frozen container

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param name [String] Container name @param options [Hash] Additional data to be passed @option options [Boolean] :sync If false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @option options [Fixnum] :timeout Time after which the operation is considered to have failed (default: no timeout) @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Resume container

Hyperkit.unfreeze_container("test")

@example Resume container with timeout

Hyperkit.unfreeze_container("test", timeout: 30)
# File lib/hyperkit/client/containers.rb, line 489
def unfreeze_container(name, options={})
  opts = options.slice(:timeout)
  response = put(container_state_path(name), opts.merge(action: "unfreeze")).metadata
  handle_async(response, options[:sync])
end
Also aliased as: resume_container
update_container(name, config, options={}) click to toggle source

Update the configuration of a container.

Configuration is overwritten, not merged. Accordingly, clients should first call container to obtain the current configuration of a container. The resulting object should be modified and then passed to update_container.

Note that LXD does not allow certain attributes to be changed (e.g. status, status_code, stateful, name, etc.) through this call.

@async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.

@param name [String] Container name @param config [Sawyer::Resource|Hash] Container configuration obtained from container @param options [Hash] Additional data to be passed @option options [Boolean] :sync If false, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If true, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}. @return [Sawyer::Resource] Operation or result, depending value of :sync parameter and/or {Hyperkit::Client::auto_sync}

@example Add 'eth1' device to a container

container = Hyperkit.container("test-container")
container.devices.eth1 = {nictype: "bridged", parent: "lxcbr0", type: "nic"}
Hyperkit.update_container("test-container", container)

@example Change container to be ephemeral (i.e. it will be deleted when stopped)

container = Hyperkit.container("test-container")
container.ephemeral = true
Hyperkit.update_container("test-container", container)

@example Change container's AppArmor profile to 'unconfined'.

container = Hyperkit.container("test-container")

# Note: due to a bug in Sawyer::Resource, the following will fail
container.config[:"raw.lxc"] = "lxc.aa_profile=unconfined"

# Instead, convert 'config' to a Hash, and update the Hash
container.config = container.config.to_hash
container.config["raw.lxc"] = "lxc.aa_profile=unconfined"

Hyperkit.update_container("test-container", container)
# File lib/hyperkit/client/containers.rb, line 248
def update_container(name, config, options={})

  config = config.to_hash
  config[:config] = stringify_hash(config[:config]) if config[:config]

  response = put(container_path(name), config).metadata
  handle_async(response, options[:sync])
end
write_file(container, dest_file, options={}) { |io| ... } click to toggle source

Write to a file in a container

@param container [String] Container name @param dest_file [String] Path to the output file in the container @param options [Hash] Additional data to be passed @option options [Fixnum] :uid Owner to assign to the file @option options [Fixnum] :gid Group to assign to the file @option options [Fixnum] :mode File permissions (in octal) to assign to the file @option options [Fixnum] :content Content to write to the file (if no block given) @yieldparam io [StringIO] IO to be used to write to the file from a block @return [Sawyer::Resource]

@example Write string “hello” to /tmp/test.txt in container test-container

Hyperkit.write_file("test-container", "/tmp/test.txt", content: "hello")

@example Write to file using a block

Hyperkit.write_file("test-container", "/tmp/test.txt") do |io|
  io.print "Hello "
  io.puts "world"
end

@example Assign uid, gid, and mode to a file:

Hyperkit.write_file("test-container",
  "/tmp/test.txt",
  content: "hello",
  uid: 1000,
  gid: 1000,
  mode: 0644
)
# File lib/hyperkit/client/containers.rb, line 896
def write_file(container, dest_file, options={}, &block)

  headers = { "Content-Type" => "application/octet-stream" }
  headers["X-LXD-uid"] = options[:uid].to_s if options[:uid]
  headers["X-LXD-gid"] = options[:gid].to_s if options[:gid]
  headers["X-LXD-mode"] = options[:mode].to_s(8).rjust(4, "0") if options[:mode]

  if ! block_given?
    content = options[:content].to_s
  else
    io = StringIO.new
    yield io
    io.rewind
    content = io.read
  end

  post(file_path(container, dest_file), {
    raw_body: content,
    headers: headers
  })

end

Private Instance Methods

container_path(name) click to toggle source
# File lib/hyperkit/client/containers.rb, line 1025
def container_path(name)
  File.join(containers_path, name)
end
container_source_attribute(options) click to toggle source
# File lib/hyperkit/client/containers.rb, line 1042
def container_source_attribute(options)

  [:fingerprint, :alias, :properties].each do |attr|
    return options.slice(attr) if options[attr]
  end

  {}

end
container_state_path(name) click to toggle source
# File lib/hyperkit/client/containers.rb, line 1021
def container_state_path(name)
  File.join(container_path(name), "state")
end
containers_path() click to toggle source
# File lib/hyperkit/client/containers.rb, line 1029
def containers_path
  "/1.0/containers"
end
empty_container_options(name, options) click to toggle source
# File lib/hyperkit/client/containers.rb, line 1052
def empty_container_options(name, options)
  opts = {
    source: {
      type: "none"
    }
  }.merge(extract_container_options(name, options))

  [:alias, :certificate, :fingerprint, :properties, :protocol, :secret, :server].each do |prop|
    if ! (options.keys & [prop]).empty?
      raise Hyperkit::InvalidImageAttributes.new("empty: true is not compatible with the #{prop} option")
    end
  end

  opts

end
extract_container_options(name, options) click to toggle source
# File lib/hyperkit/client/containers.rb, line 1033
def extract_container_options(name, options)
  opts = options.slice(:architecture, :profiles, :ephemeral, :config, :devices).
                 merge({ name: name })

  opts[:config] = stringify_hash(opts[:config]) if opts[:config]

  opts
end
file_path(container, file) click to toggle source
# File lib/hyperkit/client/containers.rb, line 1009
def file_path(container, file)
  File.join(container_path(container), "files") + "?path=#{file}"
end
local_image_container_options(name, source, options) click to toggle source
# File lib/hyperkit/client/containers.rb, line 1088
def local_image_container_options(name, source, options)

  opts = {
    source: {
      type: "image"
    }.merge(source)
  }.merge(extract_container_options(name, options))

  if ! (options.keys & REMOTE_IMAGE_ARGS).empty?
    raise Hyperkit::InvalidImageAttributes.new(":protocol, :certificate, and :secret only apply when :server is also passed")
  end

  opts

end
log_path(container, log) click to toggle source
# File lib/hyperkit/client/containers.rb, line 1001
def log_path(container, log)
  File.join(logs_path(container), log)
end
logs_path(container) click to toggle source
# File lib/hyperkit/client/containers.rb, line 1005
def logs_path(container)
  File.join(container_path(container), "logs")
end
remote_image_container_options(name, source, options) click to toggle source
# File lib/hyperkit/client/containers.rb, line 1070
def remote_image_container_options(name, source, options)

  opts = {
    source: {
      type: "image",
      mode: "pull"
    }.merge(options.slice(*REMOTE_IMAGE_ARGS)).merge(source)

  }.merge(extract_container_options(name, options))

  if options[:protocol] && ! %w[lxd simplestreams].include?(options[:protocol])
    raise Hyperkit::InvalidProtocol.new("Invalid protocol.  Valid choices: lxd, simplestreams")
  end

  opts

end
snapshot_path(container, snapshot) click to toggle source
# File lib/hyperkit/client/containers.rb, line 1013
def snapshot_path(container, snapshot)
  File.join(snapshots_path(container), snapshot)
end
snapshots_path(name) click to toggle source
# File lib/hyperkit/client/containers.rb, line 1017
def snapshots_path(name)
  File.join(container_path(name), "snapshots")
end