Module: Hyperkit::Client::Containers

Included in:
Hyperkit::Client
Defined in:
lib/hyperkit/client/containers.rb

Overview

Methods for the containers API

Retrieval collapse

Creation collapse

Editing collapse

Commands collapse

Deletion collapse

State collapse

Migration collapse

Snapshots collapse

Files collapse

Logs collapse

Instance Method Details

#container(name) ⇒ Sawyer::Resource

Get information on a container

Examples:

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
}

Parameters:

  • name (String)

    Container name

Returns:

  • (Sawyer::Resource)

    Container information



64
65
66
# File 'lib/hyperkit/client/containers.rb', line 64

def container(name)
  get(container_path(name)).
end

#container_state(name) ⇒ Sawyer::Resource

Retrieve the current state of a container

Examples:

Get container state

Hyperkit.container_state("test-container") #=> {
}

Parameters:

  • name (String)

    Container name

Returns:

  • (Sawyer::Resource)

    Container state



362
363
364
# File 'lib/hyperkit/client/containers.rb', line 362

def container_state(name)
  get(container_state_path(name)).
end

#containersArray<String>

List of containers on the server (public or private)

Examples:

Get list of containers

Hyperkit.containers #=> ["container1", "container2", "container3"]

Returns:

  • (Array<String>)

    An array of container names



22
23
24
25
# File 'lib/hyperkit/client/containers.rb', line 22

def containers
  response = get(containers_path)
  response..map { |path| path.split('/').last }
end

#copy_container(source_name, target_name, options = {}) ⇒ Sawyer::Resource

Create a copy of an existing local container.

Examples:

Copy container

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

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"
  }
)

Copy container and apply profiles to it

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

Create container from a publicly-accessible remote image

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

Parameters:

  • source_name (String)

    Source container name

  • target_name (String)

    Target container name

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

  • :architecture (String)

    Architecture of the container (e.g. x86_64). By default, this will be obtained from the image metadata

  • :config (Hash)

    Container configuration

  • :ephemeral (Boolean)

    Whether to make the container ephemeral (i.e. delete it when it is stopped; default: false)

  • :profiles (Array)

    List of profiles to be applied to the container (default: [])

  • :sync (Boolean)

    If false, returns an asynchronous operation that must be passed to Operations#wait_for_operation. If true, automatically waits and returns the result of the operation. Defaults to value of Hyperkit::Configurable#auto_sync.

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



190
191
192
193
194
195
196
197
198
199
200
201
202
# 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).
  handle_async(response, options[:sync])

end

#create_container(name, options = {}) ⇒ Sawyer::Resource

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

Examples:

Create container from image specified by alias

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

Create container from image specified by fingerprint

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

Create container from image specified by fingerprint prefix

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

Create container based on most recent match of image properties

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

Create an empty container

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

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"
  }
)

Create container and apply profiles to it

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

Create container from a publicly-accessible remote image

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

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")

Parameters:

  • name (String)

    Container name

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

  • :alias (String)

    Alias of the source image. Either :alias, :fingerprint, :properties, or empty: true must be specified.

  • :architecture (String)

    Architecture of the container (e.g. x86_64). By default, this will be obtained from the image metadata

  • :certificate (String)

    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.

  • :config (Hash)

    Container configuration

  • :ephemeral (Boolean)

    Whether to make the container ephemeral (i.e. delete it when it is stopped; default: false)

  • :empty (Boolean)

    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.

  • :fingerprint (String)

    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.

  • :profiles (Array)

    List of profiles to be applied to the container (default: [])

  • :properties (String)

    Properties of the source image. Either :alias, :fingerprint, :properties, or empty: true must be specified.

  • :protocol (String)

    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.

  • :secret (String)

    Secret to use to retrieve the image. This option is valid only when transferring an image from a remote server using the :server option.

  • :server (String)

    URL of remote server from which to obtain image. By default, the image will be obtained from the client’s api_endpoint.

  • :sync (Boolean)

    If false, returns an asynchronous operation that must be passed to Operations#wait_for_operation. If true, automatically waits and returns the result of the operation. Defaults to value of Hyperkit::Configurable#auto_sync.

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# 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).
  handle_async(response, options[:sync])
end

#create_snapshot(container, snapshot, options = {}) ⇒ Sawyer::Resource

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.

Examples:

Create stateless snapshot for container ‘test’

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

Create snapshot and save runtime state for running container ‘test’

Hyperkit.create_snapshot("test", "snap1", stateful: true)

Parameters:

  • container (String)

    Container name

  • snapshot (String)

    Snapshot name

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

  • :stateful (Boolean)

    Whether to save runtime state for a running container (requires CRIU on the server; default: <code>false</false>)

  • :sync (Boolean)

    If false, returns an asynchronous operation that must be passed to Operations#wait_for_operation. If true, automatically waits and returns the result of the operation. Defaults to value of Hyperkit::Configurable#auto_sync.

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



756
757
758
759
760
761
# 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).
  handle_async(response, options[:sync])
end

#delete_container(name, options = {}) ⇒ Sawyer::Resource

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

Examples:

Delete container “test”

Hyperkit.delete_container("test")

Parameters:

  • name (String)

    Container name

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



345
346
347
348
# File 'lib/hyperkit/client/containers.rb', line 345

def delete_container(name, options={})
  response = delete(container_path(name)).
  handle_async(response, options[:sync])
end

#delete_log(container, log) ⇒ Object

Delete a container’s log

Examples:

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

Hyperkit.delete_log("test-container", "lxc.log")

Parameters:

  • container (String)

    Container name

  • log (String)

    Filename of log to delete



991
992
993
# File 'lib/hyperkit/client/containers.rb', line 991

def delete_log(container, log)
  delete(log_path(container, log))
end

#delete_snapshot(container, snapshot, options = {}) ⇒ Sawyer::Resource

Delete a snapshot

Examples:

Delete snapshot “snap” from container “test”

Hyperkit.delete_snapshot("test", "snap")

Parameters:

  • container (String)

    Container name

  • snapshot (String)

    Snapshot name

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



776
777
778
779
# File 'lib/hyperkit/client/containers.rb', line 776

def delete_snapshot(container, snapshot, options={})
  response = delete(snapshot_path(container, snapshot)).
  handle_async(response, options[:sync])
end

#execute_command(container, command, options = {}) ⇒ Sawyer::Resource Also known as: run_command

Execute a command in a container

Examples:

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

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

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

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

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
  }
)

Parameters:

  • container (String)

    Container name

  • command (Array|String)

    Command to execute

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

  • :environment (Hash)

    Environment variables to set prior to command execution

  • :sync (Boolean)

    If false, returns an asynchronous operation that must be passed to Operations#wait_for_operation. If true, automatically waits and returns the result of the operation. Defaults to value of Hyperkit::Configurable#auto_sync.

  • :wait_for_websocket (Boolean)

    If true block and wait for a websocket connection to start.

  • :interactive (Boolean)

    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.

  • :record_output (Boolean)

    If true, captures the output of stdout and stderr.

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
# 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
  }).

  handle_async(response, options[:sync])

end

#freeze_container(name, options = {}) ⇒ Sawyer::Resource Also known as: pause_container, suspend_container

Freeze (suspend) a running container

Examples:

Suspend container

Hyperkit.freeze_container("test")

Suspend container with timeout

Hyperkit.freeze_container("test", timeout: 30)

Parameters:

  • name (String)

    Container name

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

  • :sync (Boolean)

    If false, returns an asynchronous operation that must be passed to Operations#wait_for_operation. If true, automatically waits and returns the result of the operation. Defaults to value of Hyperkit::Configurable#auto_sync.

  • :timeout (Fixnum)

    Time after which the operation is considered to have failed (default: no timeout)

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



465
466
467
468
469
# 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")).
  handle_async(response, options[:sync])
end

#init_migration(container, snapshot = nil) ⇒ Sawyer::Resource

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.

Examples:

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"
}

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"
}

Parameters:

  • name (String)

    Container name

Returns:

  • (Sawyer::Resource)

    Source data to be passed to #migrate



552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
# 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...to_hash,
    },
    certificate: get("/1.0")..environment.certificate,
    snapshot: ! snapshot.nil?
  }

  Sawyer::Resource.new(response.agent, source_data)
end

#log(container, log) ⇒ String

Retrieve the contents of a log for a container

Examples:

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

Hyperkit.log("test-container", "lxc.log")

Parameters:

  • container (String)

    Container name

  • log (String)

    Log filename

Returns:

  • (String)

    The contents of the log



980
981
982
# File 'lib/hyperkit/client/containers.rb', line 980

def log(container, log)
  get(log_path(container, log))
end

#logs(container) ⇒ Array<String>

Retrieve a list of logs for a container

Examples:

Get list of logs for container “test-container”

Hyperkit.logs("test-container")

Parameters:

  • container (String)

    Container name

Returns:

  • (Array<String>)

    An array of log filenames



967
968
969
970
# File 'lib/hyperkit/client/containers.rb', line 967

def logs(container)
  response = get(logs_path(container))
  response..map { |path| path.sub(logs_path(container) + '/', '') }
end

#migrate(source, dest_name, options = {}) ⇒ Sawyer::Resource

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.

Examples:

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")

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)

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])

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])

Parameters:

  • source (Sawyer::Resource)

    Source data retrieve from the remote server with #init_migration

  • dest_name (String)

    Name of the new container

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

  • :architecture (String)

    Architecture of the container (e.g. x86_64). By default, this will be obtained from the image metadata

  • :certificate (String)

    PEM certificate of the source server. If not specified, defaults to the certificate returned by the source server in the source parameter.

  • :config (Hash)

    Container configuration

  • :ephemeral (Boolean)

    Whether to make the container ephemeral (i.e. delete it when it is stopped; default: false)

  • :move (Boolean)

    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)

  • :profiles (Array)

    List of profiles to be applied to the container (default: [])

  • :sync (Boolean)

    If false, returns an asynchronous operation that must be passed to Operations#wait_for_operation. If true, automatically waits and returns the result of the operation. Defaults to value of Hyperkit::Configurable#auto_sync.

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
# 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).
  handle_async(response, options[:sync])
end

#pull_file(container, source_file, dest) ⇒ String

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.

Examples:

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

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

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

Hyperkit.pull_file("test", "/etc/passwd", StringIO.new) #=> <StringIO:0x007fd196061a70>

Parameters:

  • container (String)

    Container name

  • source_file (String)

    Full path to a file within the container

  • dest (String, IO)

    Full path of desired output file (will be created/overwritten), or an IO object to write to

Returns:

  • (String)

    Full path to the local output file



847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
# 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 = {}) ⇒ Sawyer::Resource

Copy a file from the local system to container

Examples:

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

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

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

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

Assign uid, gid, and mode to a file:

Hyperkit.push_file("/tmp/test.txt",
  "test-container",
  "/etc/passwd",
  uid: 1000,
  gid: 1000,
  mode: 0644
)

Parameters:

  • container (String)

    Container name

  • source_file (String)

    Full path to a file within the container

  • dest (String, IO)

    Full path of desired output file (will be created/overwritten), or an IO object to write to

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

  • :uid (Fixnum)

    Owner to assign to the file

  • :gid (Fixnum)

    Group to assign to the file

  • :mode (Fixnum)

    File permissions (in octal) to assign to the file

Returns:

  • (Sawyer::Resource)


944
945
946
947
948
949
950
951
952
953
954
# 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) ⇒ String

Read the contents of a file in a container

Examples:

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

Hyperkit.read_file("test", "/etc/hostname") #=> "test-container.example.com"

Parameters:

  • container (String)

    Container name

  • file (String)

    Full path to a file within the container

Returns:

  • (String)

    The contents of the file



830
831
832
# 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 = {}) ⇒ Sawyer::Resource

Rename a container

Examples:

Rename container “test” to “test2”

Hyperkit.rename_container("test", "test2")

Parameters:

  • old_name (String)

    Existing container name

  • new_name (String)

    New container name

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



269
270
271
272
# 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 }).
  handle_async(response, options[:sync])
end

#rename_snapshot(container, old_name, new_name, options = {}) ⇒ Sawyer::Resource

Rename a snapshot

Examples:

Rename snapshot “test/snap1” to “snap2”

Hyperkit.rename_snapshot("test", "snap1", "snap2")

Parameters:

  • container (String)

    Container name

  • old_name (String)

    Existing snapshot name

  • new_name (String)

    New snapshot name

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



794
795
796
797
# 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 }).
  handle_async(response, options[:sync])
end

#restart_container(name, options = {}) ⇒ Sawyer::Resource

Restart a running container

Examples:

Restart container

Hyperkit.restart_container("test")

Restart container forcefully

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

Restart container with timeout

Hyperkit.restart_container("test", timeout: 30)

Parameters:

  • name (String)

    Container name

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

  • :force (Boolean)

    Whether to force the operation by killing the container

  • :sync (Boolean)

    If false, returns an asynchronous operation that must be passed to Operations#wait_for_operation. If true, automatically waits and returns the result of the operation. Defaults to value of Hyperkit::Configurable#auto_sync.

  • :timeout (Fixnum)

    Time after which the operation is considered to have failed (default: no timeout)

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



444
445
446
447
448
# 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")).
  handle_async(response, options[:sync])
end

#restore_snapshot(container, snapshot, options = {}) ⇒ Sawyer::Resource Also known as: revert_to_snapshot

Restore a snapshot

Examples:

Restore container “test” back to snapshot “snap1”

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

Parameters:

  • container (String)

    Container name

  • snapshot (String)

    Name of snapshot to restore

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



811
812
813
814
# File 'lib/hyperkit/client/containers.rb', line 811

def restore_snapshot(container, snapshot, options={})
  response = put(container_path(container), { restore: snapshot }).
  handle_async(response, options[:sync])
end

#snapshot(container, snapshot) ⇒ Sawyer::Resource

Get information on a snapshot

Examples:

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
}

Parameters:

  • container (String)

    Container name

  • Snapshot (String)

    Snapshot name

Returns:

  • (Sawyer::Resource)

    Snapshot information



729
730
731
# File 'lib/hyperkit/client/containers.rb', line 729

def snapshot(container, snapshot)
  get(snapshot_path(container, snapshot)).
end

#snapshots(container) ⇒ Array<String>

List of snapshots for a container

Examples:

Get list of snapshots for container “test”

Hyperkit.snapshots("test") #=> ["snap1", "snap2", "snap3"]

Parameters:

  • container (String)

    Container name

Returns:

  • (Array<String>)

    An array of snapshot names



686
687
688
689
# File 'lib/hyperkit/client/containers.rb', line 686

def snapshots(container)
  response = get snapshots_path(container)
  response..map { |path| path.split('/').last }
end

#start_container(name, options = {}) ⇒ Sawyer::Resource

Start a container

Examples:

Start container

Hyperkit.start_container("test")

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)

Start container with a timeout

Hyperkit.start_container("test", timeout: 30)

Parameters:

  • name (String)

    Container name

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

  • :stateful (Boolean)

    Whether to restore previously saved runtime state (default: <code>false</false>)

  • :sync (Boolean)

    If false, returns an asynchronous operation that must be passed to Operations#wait_for_operation. If true, automatically waits and returns the result of the operation. Defaults to value of Hyperkit::Configurable#auto_sync.

  • :timeout (Fixnum)

    Time after which the operation is considered to have failed (default: no timeout)

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



389
390
391
392
393
# 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")).
  handle_async(response, options[:sync])
end

#stop_container(name, options = {}) ⇒ Sawyer::Resource

Stop a container

Examples:

Stop container

Hyperkit.stop_container("test")

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)

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

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

Parameters:

  • name (String)

    Container name

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

  • :force (Boolean)

    Whether to force the operation by killing the container

  • :stateful (Boolean)

    Whether to restore previously saved runtime state (default: <code>false</false>)

  • :sync (Boolean)

    If false, returns an asynchronous operation that must be passed to Operations#wait_for_operation. If true, automatically waits and returns the result of the operation. Defaults to value of Hyperkit::Configurable#auto_sync.

  • :timeout (Fixnum)

    Time after which the operation is considered to have failed (default: no timeout)

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



419
420
421
422
423
# 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")).
  handle_async(response, options[:sync])
end

#unfreeze_container(name, options = {}) ⇒ Sawyer::Resource Also known as: resume_container

Unfreeze (resume) a frozen container

Examples:

Resume container

Hyperkit.unfreeze_container("test")

Resume container with timeout

Hyperkit.unfreeze_container("test", timeout: 30)

Parameters:

  • name (String)

    Container name

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

  • :sync (Boolean)

    If false, returns an asynchronous operation that must be passed to Operations#wait_for_operation. If true, automatically waits and returns the result of the operation. Defaults to value of Hyperkit::Configurable#auto_sync.

  • :timeout (Fixnum)

    Time after which the operation is considered to have failed (default: no timeout)

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



489
490
491
492
493
# 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")).
  handle_async(response, options[:sync])
end

#update_container(name, config, options = {}) ⇒ Sawyer::Resource

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.

Examples:

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)

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)

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)

Parameters:

  • name (String)

    Container name

  • config (Sawyer::Resource|Hash)

    Container configuration obtained from #container

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

Returns:

  • (Sawyer::Resource)

    Operation or result, depending value of :sync parameter and/or auto_sync

Asynchronous:



248
249
250
251
252
253
254
255
# 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).
  handle_async(response, options[:sync])
end

#write_file(container, dest_file, options = {}) {|io| ... } ⇒ Sawyer::Resource

Write to a file in a container

Examples:

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

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

Write to file using a block

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

Assign uid, gid, and mode to a file:

Hyperkit.write_file("test-container",
  "/tmp/test.txt",
  content: "hello",
  uid: 1000,
  gid: 1000,
  mode: 0644
)

Parameters:

  • container (String)

    Container name

  • dest_file (String)

    Path to the output file in the container

  • options (Hash) (defaults to: {})

    Additional data to be passed

Options Hash (options):

  • :uid (Fixnum)

    Owner to assign to the file

  • :gid (Fixnum)

    Group to assign to the file

  • :mode (Fixnum)

    File permissions (in octal) to assign to the file

  • :content (Fixnum)

    Content to write to the file (if no block given)

Yield Parameters:

  • io (StringIO)

    IO to be used to write to the file from a block

Returns:

  • (Sawyer::Resource)


896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
# 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