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
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
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
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
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 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 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 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 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 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 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 true
a 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
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
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
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
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 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
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
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 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 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 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 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 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
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
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 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 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
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
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 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
# File lib/hyperkit/client/containers.rb, line 1025 def container_path(name) File.join(containers_path, name) end
# 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
# File lib/hyperkit/client/containers.rb, line 1021 def container_state_path(name) File.join(container_path(name), "state") end
# File lib/hyperkit/client/containers.rb, line 1029 def containers_path "/1.0/containers" end
# 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
# 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 lib/hyperkit/client/containers.rb, line 1009 def file_path(container, file) File.join(container_path(container), "files") + "?path=#{file}" end
# 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
# File lib/hyperkit/client/containers.rb, line 1001 def log_path(container, log) File.join(logs_path(container), log) end
# File lib/hyperkit/client/containers.rb, line 1005 def logs_path(container) File.join(container_path(container), "logs") end
# 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
# File lib/hyperkit/client/containers.rb, line 1013 def snapshot_path(container, snapshot) File.join(snapshots_path(container), snapshot) end
# File lib/hyperkit/client/containers.rb, line 1017 def snapshots_path(name) File.join(container_path(name), "snapshots") end