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



358
359
360
# File 'lib/hyperkit/client/containers.rb', line 358

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:



752
753
754
755
756
757
# File 'lib/hyperkit/client/containers.rb', line 752

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:



341
342
343
344
# File 'lib/hyperkit/client/containers.rb', line 341

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



973
974
975
# File 'lib/hyperkit/client/containers.rb', line 973

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:



772
773
774
775
# File 'lib/hyperkit/client/containers.rb', line 772

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.

Returns:

  • (Sawyer::Resource)

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

Asynchronous:



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/hyperkit/client/containers.rb', line 305

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" => false,
    interactive: 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:



461
462
463
464
465
# File 'lib/hyperkit/client/containers.rb', line 461

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



548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
# File 'lib/hyperkit/client/containers.rb', line 548

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



962
963
964
# File 'lib/hyperkit/client/containers.rb', line 962

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



949
950
951
952
# File 'lib/hyperkit/client/containers.rb', line 949

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:



619
620
621
622
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
# File 'lib/hyperkit/client/containers.rb', line 619

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

Parameters:

  • container (String)

    Container name

  • source_file (String)

    Full path to a file within the container

  • dest_file (String)

    Full path of desired output file (will be created/overwritten)

Returns:

  • (String)

    Full path to the local output file



840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
# File 'lib/hyperkit/client/containers.rb', line 840

def pull_file(container, source_file, dest_file)
  contents = get(file_path(container, source_file), url_encode: false)
  headers = last_response.headers

  File.open(dest_file, "wb") do |f|
    f.write(contents)
  end

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

  dest_file

end

#push_file(source_file, 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")

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_file (String)

    Full path of desired output file (will be created/overwritten)

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


930
931
932
933
934
935
936
# File 'lib/hyperkit/client/containers.rb', line 930

def push_file(source_file, container, dest_file, options={})

  write_file(container, dest_file, options) do |f|
    f.write File.read(source_file)
  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



826
827
828
# File 'lib/hyperkit/client/containers.rb', line 826

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:



790
791
792
793
# File 'lib/hyperkit/client/containers.rb', line 790

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:



440
441
442
443
444
# File 'lib/hyperkit/client/containers.rb', line 440

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:



807
808
809
810
# File 'lib/hyperkit/client/containers.rb', line 807

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



725
726
727
# File 'lib/hyperkit/client/containers.rb', line 725

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



682
683
684
685
# File 'lib/hyperkit/client/containers.rb', line 682

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:



385
386
387
388
389
# File 'lib/hyperkit/client/containers.rb', line 385

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:



415
416
417
418
419
# File 'lib/hyperkit/client/containers.rb', line 415

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:



485
486
487
488
489
# File 'lib/hyperkit/client/containers.rb', line 485

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)


885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
# File 'lib/hyperkit/client/containers.rb', line 885

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