chef-provisioning-docker 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eb7082e943ebcd274d30997976c1049a8eda063c
4
- data.tar.gz: e453939b728683c8d9c0a74a313413a83496ec08
3
+ metadata.gz: 7cd0cd2d71b1bd1aece33ccaf51de2f3d877226b
4
+ data.tar.gz: 7ea228d6b0a5c4daf39f14d60d35d0781e637fb0
5
5
  SHA512:
6
- metadata.gz: f4852d7f066c734ecf52dc2cbe2b2121f40327d82078503c1ef98ac2ed3e2565a43c33e916fac11ac8fdaa5c726270b7c1bd805355ba8120d734e8e9d6282f40
7
- data.tar.gz: c6c7f6e92b01b9451fafb5f46db02b28f92944394aab21cc08f556d694e481f874ad95692e31a84cba4d14d0dc4756dad52036288b2955c0b645191d3661454f
6
+ metadata.gz: 2a09fbef723a466b7188f54467cb6a49bd7b3360288c90b34c945d91691660456a00e5dd47ef67392c8a754051ba6749079b5796e9f6aef3fba6a76e4264d717
7
+ data.tar.gz: 3d678d90f7e1578f5cb5eb41ed3995b9015e6a93ae7af03ff64bdb12cfdf27782a89939c0538802d30532f570ce4b8b58922ea6768f6548b6d9e92fa08a17ac1
data/README.md CHANGED
@@ -16,15 +16,16 @@ Using this , you can then define a machine similar to the following example:
16
16
 
17
17
  ```ruby
18
18
  require 'chef/provisioning/docker_driver'
19
+ with_driver 'docker'
19
20
 
20
21
  machine 'wario' do
21
- recipe 'openssh::default'
22
+ recipe 'openssh::default'
22
23
 
23
- machine_options :docker_options => {
24
- :base_image => {
25
- :name => 'ubuntu',
26
- :repository => 'ubuntu',
27
- :tag => '14.04'
24
+ machine_options docker_options: {
25
+ base_image: {
26
+ name: 'ubuntu',
27
+ repository: 'ubuntu',
28
+ tag: '14.04'
28
29
  },
29
30
  :command => '/usr/sbin/sshd -p 8022 -D',
30
31
 
@@ -61,7 +62,12 @@ machine 'wario' do
61
62
  # if you need to keep stdin open (i.e docker run -i)
62
63
  # :keep_stdin_open => true
63
64
 
65
+ },
66
+ # optional, default timeout is 600
67
+ docker_connection: {
68
+ :read_timeout => 1000,
64
69
  }
70
+
65
71
  end
66
72
  ```
67
73
 
@@ -95,6 +101,5 @@ end
95
101
  ```
96
102
 
97
103
  This will create a docker container based on Ubuntu 14.04 and
98
- then execute the Apache recipe and run the /usr/sbin/httpd command
104
+ then execute the openssh recipe and run the /usr/sbin/sshd command
99
105
  as the container's run command.
100
-
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
14
14
 
15
15
  s.add_dependency 'chef'
16
16
  s.add_dependency 'chef-provisioning', '~> 1.0'
17
- s.add_dependency 'docker-api'
17
+ s.add_dependency 'docker-api', '~> 1.26', '>= 1.26.2'
18
18
  s.add_dependency 'minitar'
19
19
  s.add_dependency 'sys-proctable'
20
20
 
@@ -1,4 +1,5 @@
1
1
  require 'chef/provisioning/machine/unix_machine'
2
+ require 'chef/provisioning/docker_driver/docker_run_options'
2
3
 
3
4
  class Chef
4
5
  module Provisioning
@@ -9,29 +10,214 @@ module DockerDriver
9
10
  # Options is expected to contain the optional keys
10
11
  # :command => the final command to execute
11
12
  # :ports => a list of port numbers to listen on
12
- def initialize(machine_spec, transport, convergence_strategy, command = nil)
13
+ def initialize(machine_spec, transport, convergence_strategy, connection, command = nil)
13
14
  super(machine_spec, transport, convergence_strategy)
14
15
  @command = command
15
16
  @transport = transport
17
+ @connection = connection
18
+ end
19
+
20
+ def setup_convergence(action_handler)
21
+ # Build a converge container to converge in
22
+ transport.container = build_converge_container(action_handler)
23
+ unless transport.container.info['State']['Running']
24
+ action_handler.perform_action "start converge container chef-converge.#{machine_spec.name}" do
25
+ transport.container.start!
26
+ end
27
+ end
28
+ super(action_handler)
29
+ # Commit after convergence setup (such as the install of Chef)
30
+ # to break up the cost of the commit and avoid read timeouts
31
+ transport.container.commit
16
32
  end
17
33
 
18
34
  def converge(action_handler)
19
- super action_handler
20
- Chef::Log.debug("DockerContainerMachine converge complete, executing #{@command} in #{@container_name}")
21
- image = transport.container.commit(
22
- 'repo' => 'chef',
23
- 'tag' => machine_spec.reference['container_name']
35
+ # First, grab and start the converge container if it's there ...
36
+ transport.container = converge_container_for(machine_spec)
37
+ if !transport.container
38
+ raise "No converge container found! Did you run `:converge` without first running `:setup`?"
39
+ end
40
+ unless transport.container.info['State']['Running']
41
+ action_handler.perform_action "start converge container chef-converge.#{machine_spec.name}" do
42
+ transport.container.start!
43
+ end
44
+ end
45
+
46
+ # Then, converge ...
47
+ super(action_handler)
48
+
49
+ # Save the converged image ...
50
+ converged_image = commit_converged_image(action_handler, machine_spec, transport.container)
51
+
52
+ # Build the new container
53
+ transport.container = create_container(action_handler, machine_spec, converged_image)
54
+
55
+ # Finally, start it!
56
+ action_handler.perform_action "start container #{machine_spec.name}" do
57
+ transport.container.start!
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def container_config(action_handler, machine_spec)
64
+ docker_options = machine_spec.reference['docker_options'] || {}
65
+
66
+ # We're going to delete things to make it easier on ourselves, back it up
67
+ docker_options = docker_options.dup
68
+
69
+ # Bring in from_image
70
+ if machine_spec.from_image
71
+ docker_options['base_image'] ||= {}
72
+ docker_options['base_image']['name'] = machine_spec.from_image
73
+ end
74
+
75
+ # Respect :container_config
76
+ config = stringize_keys(docker_options.delete('container_config') || {})
77
+
78
+ # Respect :base_image
79
+ image = base_image(action_handler, docker_options.delete('base_image'))
80
+ config['Image'] = image if image
81
+
82
+ # Respect everything else
83
+ DockerRunOptions.include_command_line_options_in_container_config(config, docker_options)
84
+ end
85
+
86
+ # Get the converge container for this machine
87
+ def converge_container_for(machine_spec)
88
+ begin
89
+ Docker::Container.get("chef-converge.#{machine_spec.name}", {}, @connection)
90
+ rescue Docker::Error::NotFoundError
91
+ end
92
+ end
93
+
94
+ def container_for(machine_spec)
95
+ begin
96
+ Docker::Container.get(machine_spec.name, {}, @connection)
97
+ rescue Docker::Error::NotFoundError
98
+ end
99
+ end
100
+
101
+ # Builds a container that has the same properties as the final container,
102
+ # but with a couple of tweaks to allow processes to run and converge the
103
+ # container.
104
+ def build_converge_container(action_handler)
105
+ # If a converge container already exists, do nothing. TODO check if it's different!!!
106
+ converge_container = converge_container_for(machine_spec)
107
+ if converge_container
108
+ return converge_container
109
+ end
110
+
111
+ # Create a chef-capable container (just like the final one, but with --net=host
112
+ # and a command that keeps it open). Base it on the image.
113
+ config = container_config(action_handler, machine_spec)
114
+ config.merge!(
115
+ 'name' => "chef-converge.#{machine_spec.reference['container_name']}",
116
+ 'Cmd' => [ "/bin/sh", "-c", "while true;do sleep 1000; done" ],
24
117
  )
25
- machine_spec.reference['image_id'] = image.id
118
+ # If we're using Docker Toolkit, we need to use host networking for the converge
119
+ # so we can open up the port we need. Don't force it in other cases, though.
120
+ if transport.is_local_machine(URI(transport.config[:chef_server_url]).host) &&
121
+ transport.docker_toolkit_transport(@connection.url)
122
+ config['HostConfig'] ||= {}
123
+ config['HostConfig'].merge!('NetworkMode' => 'host')
124
+ # These are incompatible with NetworkMode: host
125
+ config['HostConfig'].delete('Links')
126
+ config['HostConfig'].delete('ExtraHosts')
127
+ config.delete('NetworkSettings')
128
+ end
129
+ # Don't use any resources that need to be shared (such as exposed ports)
130
+ config.delete('ExposedPorts')
131
+
132
+ Chef::Log.debug("Creating converge container with config #{config} ...")
133
+ action_handler.perform_action "create container to converge #{machine_spec.name}" do
134
+ # create deletes the name :(
135
+ Docker::Container.create(config.dup, @connection)
136
+ converge_container = Docker::Container.get(config['name'], {}, @connection)
137
+ Chef::Log.debug("Created converge container #{converge_container.id}")
138
+ end
139
+ converge_container
140
+ end
141
+
142
+ # Commit the converged container to an image. Called by converge.
143
+ def commit_converged_image(action_handler, machine_spec, converge_container)
144
+ # Commit the converged container to an image
145
+ converged_image = nil
146
+ action_handler.perform_action "commit and delete converged container for #{machine_spec.name}" do
147
+ converged_image = converge_container.commit
148
+ converge_container.stop!
149
+ converge_container.delete
150
+ end
151
+ converged_image
152
+ end
153
+
154
+ # Create the final container from the converged image
155
+ def create_container(action_handler, machine_spec, converged_image)
156
+ # Check if the container already exists.
157
+ container = container_for(machine_spec)
158
+ if container
159
+ # If it's the same image, just return; don't stop and start.
160
+ if container.info['Image'] == converged_image.id
161
+ return container
162
+ else
163
+ # If the container exists but is based on an old image, destroy it.
164
+ action_handler.perform_action "stop and delete running container for #{machine_spec.name}" do
165
+ container.stop!
166
+ container.delete
167
+ end
168
+ end
169
+ end
26
170
 
27
- if @command && transport.container.info['Config']['Cmd'].join(' ') != @command
28
- transport.container.delete(:force => true)
29
- container = image.run(Shellwords.split(@command))
30
- container.rename(machine_spec.reference['container_name'])
171
+ # Create the new container
172
+ config = container_config(action_handler, machine_spec)
173
+ config.merge!(
174
+ 'name' => machine_spec.reference['container_name'],
175
+ 'Image' => converged_image.id
176
+ )
177
+ action_handler.perform_action "create final container for #{machine_spec.name}" do
178
+ container = Docker::Container.create(config, @connection)
31
179
  machine_spec.reference['container_id'] = container.id
32
- transport.container = container
180
+ machine_spec.save(action_handler)
181
+ end
182
+ container
183
+ end
184
+
185
+ def stringize_keys(hash)
186
+ hash.each_with_object({}) do |(k,v),hash|
187
+ v = stringize_keys(v) if v.is_a?(Hash)
188
+ hash[k.to_s] = v
189
+ end
190
+ end
191
+
192
+ def base_image(action_handler, base_image_value)
193
+ case base_image_value
194
+ when Hash
195
+ params = base_image_value.dup
196
+ if !params['fromImage']
197
+ params['fromImage'] = params.delete('name')
198
+ params['fromImage'] = "#{params['fromImage']}:#{params.delete('tag')}" if params['tag']
199
+ end
200
+ when String
201
+ params = { 'fromImage' => base_image_value }
202
+ when nil
203
+ return nil
204
+ else
205
+ raise "Unexpected type #{base_image_value.class} for docker_options[:base_image]!"
206
+ end
207
+
208
+ image_name = params['fromImage']
209
+ repo, image_name = params['fromImage'].split('/', 2) if params['fromImage'].include?('/')
210
+
211
+ begin
212
+ image = Docker::Image.get(image_name, {}, @connection)
213
+ rescue Docker::Error::NotFoundError
214
+ # If it's not found, pull it.
215
+ action_handler.perform_action "pull #{params}" do
216
+ image = Docker::Image.create(params, @connection)
217
+ end
33
218
  end
34
- machine_spec.save(action_handler)
219
+
220
+ image.id
35
221
  end
36
222
  end
37
223
  end
@@ -0,0 +1,593 @@
1
+ class Chef
2
+ module Provisioning
3
+ module DockerDriver
4
+ #
5
+ # Allows the user to specify docker options that calculate the desired container config
6
+ #
7
+ # Command line options follow later in the file (search for `cli_option :command` for the first one).
8
+ #
9
+ # The API allows these settings:
10
+ #
11
+ # (https://docs.docker.com/engine/reference/api/docker_remote_api_v1.22/#create-a-container)
12
+ #
13
+ # ArgsEscaped - bool // True if command is already escaped (Windows specific)
14
+ # AttachStderr - Boolean value, attaches to stderr.
15
+ # - `docker run --attach STDERR`
16
+ # AttachStdin - Boolean value, attaches to stdin.
17
+ # - `docker run --attach STDIN`
18
+ # - `docker run --interactive`
19
+ # AttachStdout - Boolean value, attaches to stdout.
20
+ # - `docker run --attach STDOUT`
21
+ # Cmd - Command to run specified as a string or an array of strings.
22
+ # - `docker run <image> COMMAND`
23
+ # Cpuset - Deprecated please don’t use. Use CpusetCpus instead.
24
+ # Domainname - A string value containing the domain name to use for the container.
25
+ # Entrypoint - Set the entry point for the container as a string or an array of strings.
26
+ # - `docker run --entrypoint "COMMAND"`
27
+ # Env - A list of environment variables in the form of ["VAR=value"[,"VAR2=value2"]]
28
+ # - `docker --env "A=B"`
29
+ # ExposedPorts - An object mapping ports to an empty object in the form of: "ExposedPorts": { "<port>/<tcp|udp>: {}" }
30
+ # - `docker run --expose PORT`
31
+ # - `docker run --publish 8080:8081`
32
+ # HostConfig/AutoRemove - bool // Automatically remove container when it exits
33
+ # - `docker run --rm`
34
+ # HostConfig/Binds – A list of volume bindings for this container. Each volume binding is a string in one of these forms:
35
+ # - container_path to create a new volume for the container
36
+ # - host_path:container_path to bind-mount a host path into the container
37
+ # - host_path:container_path:ro to make the bind-mount read-only inside the container.
38
+ # - volume_name:container_path to bind-mount a volume managed by a volume plugin into the container.
39
+ # - volume_name:container_path:ro to make the bind mount read-only inside the container.
40
+ # - `docker run --volume /host/path:/container/path`
41
+ # HostConfig/BlkioBps - uint64 // Maximum Bytes per second for the container system drive
42
+ # HostConfig/BlkioDeviceReadBps - Limit read rate (bytes per second) from a device in the form of: "BlkioDeviceReadBps": [{"Path": "device_path", "Rate": rate}], for example: "BlkioDeviceReadBps": [{"Path": "/dev/sda", "Rate": "1024"}]"
43
+ # - `docker run --device-read-bps=/dev/sda:1mb`
44
+ # HostConfig/BlkioDeviceReadIOps - Limit read rate (IO per second) from a device in the form of: "BlkioDeviceReadIOps": [{"Path": "device_path", "Rate": rate}], for example: "BlkioDeviceReadIOps": [{"Path": "/dev/sda", "Rate": "1000"}]
45
+ # - `docker run --device-read-iops=/dev/sda:1000`
46
+ # HostConfig/BlkioDeviceWriteBps - Limit write rate (bytes per second) to a device in the form of: "BlkioDeviceWriteBps": [{"Path": "device_path", "Rate": rate}], for example: "BlkioDeviceWriteBps": [{"Path": "/dev/sda", "Rate": "1024"}]"
47
+ # - `docker run --device-write-bps=/dev/sda:1mb`
48
+ # HostConfig/BlkioDeviceWriteIOps - Limit write rate (IO per second) to a device in the form of: "BlkioDeviceWriteIOps": [{"Path": "device_path", "Rate": rate}], for example: "BlkioDeviceWriteIOps": [{"Path": "/dev/sda", "Rate": "1000"}]
49
+ # - `docker run --device-write-iops=/dev/sda:1000`
50
+ # HostConfig/BlkioIOps - uint64 // Maximum IOps for the container system drive
51
+ # HostConfig/BlkioWeight - Block IO weight (relative weight) accepts a weight value between 10 and 1000.
52
+ # - `docker run --blkio-weight 0`
53
+ # HostConfig/BlkioWeightDevice - Block IO weight (relative device weight) in the form of: "BlkioWeightDevice": [{"Path": "device_path", "Weight": weight}]
54
+ # - `docker run --blkio-weight-device path:weight`
55
+ # HostConfig/CapAdd - A list of kernel capabilities to add to the container.
56
+ # - `docker run --cap-add capability`
57
+ # HostConfig/CapDrop - A list of kernel capabilities to drop from the container.
58
+ # - `docker run --cap-drop capability`
59
+ # HostConfig/CgroupParent - Path to cgroups under which the container’s cgroup is created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups are created if they do not already exist.
60
+ # - `docker run --cgroup-parent parent`
61
+ # HostConfig/ConsoleSize - [2]int // Initial console size
62
+ # HostConfig/ContainerIDFile - string // File (path) where the containerId is written
63
+ # - `docker run --cidfile file`
64
+ # HostConfig/CpuPeriod - The length of a CPU period in microseconds.
65
+ # - `docker run --cpu-period 0`
66
+ # HostConfig/CpuQuota - Microseconds of CPU time that the container can get in a CPU period.
67
+ # - `docker run --cpu-quota 0`
68
+ # HostConfig/CpuShares - An integer value containing the container’s CPU Shares (ie. the relative weight vs other containers).
69
+ # - `docker run --cpu-shares 0`
70
+ # HostConfig/CpusetCpus - String value containing the cgroups CpusetCpus to use.
71
+ # - `docker run --cpuset-cpus 0-3`
72
+ # HostConfig/CpusetMems - Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.
73
+ # - `docker run --cpuset-mems 0-3`
74
+ # HostConfig/Devices - A list of devices to add to the container specified as a JSON object in the form { "PathOnHost": "/dev/deviceName", "PathInContainer": "/dev/deviceName", "CgroupPermissions": "mrw"}
75
+ # - `docker run --device path_on_host:path_in_container:cgroup_permissions`
76
+ # HostConfig/DiskQuota - int64 // Disk limit (in bytes)
77
+ # HostConfig/Dns - A list of DNS servers for the container to use.
78
+ # - `docker run --dns ip`
79
+ # HostConfig/DnsOptions - A list of DNS options
80
+ # - `docker run --dns-opt a=b`
81
+ # HostConfig/DnsSearch - A list of DNS search domains
82
+ # - `docker run --dns-opt domain`
83
+ # HostConfig/ExtraHosts - A list of hostnames/IP mappings to add to the container’s /etc/hosts file. Specified in the form ["hostname:IP"].
84
+ # - `docker run --add-host host:ip`
85
+ # HostConfig/GroupAdd - A list of additional groups that the container process will run as
86
+ # - `docker run --group-add blah`
87
+ # HostConfig/IpcMode - IpcMode // IPC namespace to use for the container
88
+ # - `docker run --ipc host`
89
+ # HostConfig/Isolation - Isolation // Isolation technology of the container (eg default, hyperv)
90
+ # - `docker run --isolation host`
91
+ # HostConfig/KernelMemory - Kernel memory limit in bytes.
92
+ # - `docker run --kernel-memory 4m`
93
+ # HostConfig/Links - A list of links for the container. Each link entry should be in the form of container_name:alias.
94
+ # - `docker run --link containername`
95
+ # HostConfig/LogConfig - Log configuration for the container, specified as a JSON object in the form { "Type": "<driver_name>", "Config": {"key1": "val1"}}. Available types: json-file, syslog, journald, gelf, awslogs, splunk, none. json-file logging driver.
96
+ # HostConfig/LogConfig/Type
97
+ # - `docker run --log-driver driver`
98
+ # HostConfig/LogConfig/Config
99
+ # - `docker run --log-opt a=b`
100
+ # HostConfig/Memory - Memory limit in bytes.
101
+ # - `docker run --memory 4G`
102
+ # HostConfig/MemoryReservation - Memory soft limit in bytes.
103
+ # - `docker run --memory-reservation 4G`
104
+ # HostConfig/MemorySwap - Total memory limit (memory + swap); set -1 to enable unlimited swap. You must use this with memory and make the swap value larger than memory.
105
+ # - `docker run --memory-swap 4G`
106
+ # HostConfig/MemorySwappiness - Tune a container’s memory swappiness behavior. Accepts an integer between 0 and 100.
107
+ # - `docker run --memory-swappiness 50`
108
+ # HostConfig/NetworkMode - Sets the networking mode for the container. Supported standard values are: bridge, host, none, and container:<name|id>. Any other value is taken as a custom network’s name to which this container should connect to.
109
+ # - `docker run --net host`
110
+ # HostConfig/OomKillDisable - Boolean value, whether to disable OOM Killer for the container or not.
111
+ # - `docker run --oom-kill-disable`
112
+ # HostConfig/OomScoreAdj - An integer value containing the score given to the container in order to tune OOM killer preferences.
113
+ # - `docker run --oom-score-adj 1`
114
+ # HostConfig/PidMode - PidMode // PID namespace to use for the container
115
+ # - `docker run --pid host`
116
+ # HostConfig/PidsLimit - int64 // Setting pids limit for a container
117
+ # HostConfig/PortBindings - A map of exposed container ports and the host port they should map to. A JSON object in the form { <port>/<protocol>: [{ "HostPort": "<port>" }] } Take note that port is specified as a string and not an integer value.
118
+ # - `docker run --publish 8080:8081`
119
+ # HostConfig/Privileged - Gives the container full access to the host. Specified as a boolean value.
120
+ # - `docker run --privileged`
121
+ # HostConfig/PublishAllPorts - Allocates a random host port for all of a container’s exposed ports. Specified as a boolean value.
122
+ # - `docker run --publish-all`
123
+ # HostConfig/ReadonlyRootfs - Mount the container’s root filesystem as read only. Specified as a boolean value.
124
+ # - `docker run --read-only`
125
+ # HostConfig/RestartPolicy – The behavior to apply when the container exits. The value is an object with a Name property of either "always" to always restart, "unless-stopped" to restart always except when user has manually stopped the container or "on-failure" to restart only when the container exit code is non-zero. If on-failure is used, MaximumRetryCount controls the number of times to retry before giving up. The default is not to restart. (optional) An ever increasing delay (double the previous delay, starting at 100mS) is added before each restart to prevent flooding the server.
126
+ # - `docker run --restart no`
127
+ # HostConfig/RestartPolicy/Name
128
+ # - `docker run --restart always`
129
+ # HostConfig/RestartPolicy/MaximumRetryCount
130
+ # - `docker run --restart on-failure:20`
131
+ # HostConfig/SandboxSize - uint64 // System drive will be expanded to at least this size (in bytes)
132
+ # HostConfig/SecurityOpt - A list of string values to customize labels for MLS systems, such as SELinux.
133
+ # - docker run --security-opt label:disabled
134
+ # HostConfig/ShmSize - Size of /dev/shm in bytes. The size must be greater than 0. If omitted the system uses 64MB.
135
+ # - `docker run --shm-size 4m`
136
+ # HostConfig/StorageOpt - []string // Storage driver options per container.
137
+ # HostConfig/Sysctls - map[string]string // List of Namespaced sysctls used for the container
138
+ # HostConfig/Tmpfs - map[string]string // List of tmpfs (mounts) used for the container
139
+ # HostConfig/Ulimits - A list of ulimits to set in the container, specified as { "Name": <name>, "Soft": <soft limit>, "Hard": <hard limit> }, for example: Ulimits: { "Name": "nofile", "Soft": 1024, "Hard": 2048 }
140
+ # - `docker run --ulimit /dev/sda:1024:2048`
141
+ # HostConfig/UTSMode - UTSMode // UTS namespace to use for the container
142
+ # - `docker run --uts host`
143
+ # HostConfig/UsernsMode - UsernsMode // The user namespace to use for the container
144
+ # HostConfig/VolumeDriver - Driver that this container users to mount volumes.
145
+ # - `docker run --volume-driver supervolume`
146
+ # HostConfig/VolumesFrom - A list of volumes to inherit from another container. Specified in the form <container name>[:<ro|rw>]
147
+ # - `docker run --volumes-from db`
148
+ # Hostname - A string value containing the hostname to use for the container.
149
+ # - `docker run --hostname blah`
150
+ # Image - A string specifying the image name to use for the container.
151
+ # - `docker run IMAGE_NAME ...`
152
+ # Labels - Adds a map of labels to a container. To specify a map: {"key":"value"[,"key2":"value2"]}
153
+ # - `docker run --label a=b`
154
+ # MacAddress - string // Mac Address of the container
155
+ # - `docker run --mac-address 92:d0:c6:0a:29:33`
156
+ # Mounts - An array of mount points in the container.
157
+ # NetworkDisabled - Boolean value, when true disables networking for the container
158
+ # NetworkSettings/<network>/IPAMConfig/IPv4Address
159
+ # - `docker run --ip address`
160
+ # NetworkSettings/<network>/IPAMConfig/IPv6Address
161
+ # - `docker run --ip6 address`
162
+ # NetworkSettings/<network>/Aliases
163
+ # - `docker run --net-alias blah`
164
+ # OnBuild - []string // ONBUILD metadata that were defined on the image Dockerfile
165
+ # OpenStdin - Boolean value, opens stdin,
166
+ # - `docker run --interactive`
167
+ # PublishService - string // Name of the network service exposed by the container
168
+ # StdinOnce - Boolean value, close stdin after the 1 attached client disconnects.
169
+ # StopSignal - Signal to stop a container as a string or unsigned integer. SIGTERM by default.
170
+ # - `docker run --stop-signal SIGKILL`
171
+ # Tty - Boolean value, Attach standard streams to a tty, including stdin if it is not closed.
172
+ # - `docker run --tty`
173
+ # User - A string value specifying the user inside the container.
174
+ # - `docker run --user bob:wheel`
175
+ # Volumes - map[string]struct{} // List of volumes (mounts) used for the container
176
+ # - `docker run --volume blarghle`
177
+ # WorkingDir - A string specifying the working directory for commands to run in.
178
+ # - `docker run --workdir /home/uber`
179
+ #
180
+ # The following are runtime updateable:
181
+ # HostConfig/BlkioBps
182
+ # HostConfig/BlkioIOps
183
+ # HostConfig/BlkioWeight
184
+ # HostConfig/BlkioWeightDevice
185
+ # HostConfig/BlkioDeviceReadBps
186
+ # HostConfig/BlkioDeviceWriteBps
187
+ # HostConfig/BlkioDeviceReadIOps
188
+ # HostConfig/BlkioDeviceWriteIOps
189
+ # HostConfig/CgroupParent
190
+ # HostConfig/CpuPeriod
191
+ # HostConfig/CpuQuota
192
+ # HostConfig/CpuShares
193
+ # HostConfig/CpusetCpus
194
+ # HostConfig/CpusetMems
195
+ # HostConfig/Devices
196
+ # HostConfig/DiskQuota
197
+ # HostConfig/KernelMemory
198
+ # HostConfig/Memory
199
+ # HostConfig/MemoryReservation
200
+ # HostConfig/MemorySwap
201
+ # HostConfig/MemorySwappiness
202
+ # HostConfig/OomKillDisable
203
+ # HostConfig/PidsLimit
204
+ # HostConfig/RestartPolicy
205
+ # HostConfig/SandboxSize
206
+ # HostConfig/Ulimits
207
+ #
208
+ class DockerRunOptions
209
+ def self.include_command_line_options_in_container_config(config, docker_options)
210
+
211
+ # Grab the command line options we've begun supporting
212
+ # The following are command line equivalents for `docker run`
213
+ docker_options.each do |key, value|
214
+ # Remove -, symbolize key
215
+ key = key.to_s.gsub('-', '_').to_sym
216
+ option = cli_options[key]
217
+ if !option
218
+ raise "Unknown option in docker_options: #{key.inspect}"
219
+ end
220
+
221
+ # Figure out the new value
222
+ if option[:type] == :boolean
223
+ value == !!value
224
+ elsif option[:type] == Array
225
+ value = Array(value)
226
+ elsif option[:type] == Integer
227
+ value = parse_int(value) if value.is_a?(String)
228
+ elsif option[:type].is_a?(String)
229
+ # If it's A:B:C, translate [ "a:b:c", "d:e:f" ] -> [ { "A" => "a", "B" => "b", "C" => "c" }, { "A" => "d", "B" => "e", "C" => "f" } ]
230
+ names = option[:type].split(":")
231
+ if names.size == 2 && value.is_a?(Hash)
232
+ value.map { |key,value| { names[0] => key, names[1] => value } }
233
+ else
234
+ Array(value).map do |item|
235
+ item_values = item.split(":", names.size)
236
+ item = Hash[names.zip(item_values)]
237
+ end
238
+ end
239
+ end
240
+
241
+ option[:api].each do |api|
242
+ # Grab the parent API key so we know what we're setting
243
+ api_parent = config
244
+ api.split("/")[0..-2].each do |api_key|
245
+ api_parent[api_key] = {} if !api_parent[api_key]
246
+ api_parent = api_parent[api_key]
247
+ end
248
+ api_key = api.split("/")[-1] if api
249
+
250
+ # Bring in the current value
251
+ if option[:type] == Array || option[:type].is_a?(String)
252
+ api_parent[api_key] ||= []
253
+ api_parent[api_key] += value
254
+ else
255
+ api_parent[api_key] = value
256
+ end
257
+ end
258
+
259
+ # Call the block (if any)
260
+ if option[:block]
261
+ option[:block].call(config, value)
262
+ end
263
+ end
264
+ config
265
+ end
266
+
267
+ def self.cli_options
268
+ @cli_options ||= {}
269
+ end
270
+
271
+ def self.cli_option(option_name, type=nil, aliases: nil, api: nil, &block)
272
+ api = Array(api)
273
+ cli_options[option_name] = { type: type, api: Array(api), block: block }
274
+ Array(aliases).each do |option_name|
275
+ cli_options[option_name] = { type: type, api: Array(api), block: block }
276
+ end
277
+ end
278
+
279
+ # docker run [OPTIONS] IMAGE COMMAND
280
+ cli_option :command do |config, command|
281
+ command = Shellwords.split(command) if command.is_a?(String)
282
+ config["Cmd"] = command
283
+ end
284
+
285
+ # docker run [OPTIONS] IMAGE COMMAND
286
+ cli_option :image, api: "Image"
287
+
288
+ # -a, --attach=[] Attach to STDIN, STDOUT or STDERR
289
+ cli_option :attach, Array, aliases: :a do |config,value|
290
+ Array(value).each do |stream|
291
+ # STDIN -> Stdin
292
+ stream = stream.to_s.downcase.capitalize
293
+ config["Attach#{stream}"] = true
294
+ end
295
+ end
296
+ # --add-host=[] Add a custom host-to-IP mapping (host:ip)
297
+ cli_option :add_host, Array, api: "HostConfig/ExtraHosts", aliases: :add_hosts
298
+ # --blkio-weight=0 Block IO weight (relative weight)
299
+ cli_option :blkio_weight, api: "HostConfig/BlkioWeight"
300
+ # --blkio-weight-device=[] Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)
301
+ cli_option :blkio_weight, Array, api: "HostConfig/BlkioWeightDevice", aliases: :blkio_weights
302
+ # --cap-add=[] Add Linux capabilities
303
+ cli_option :cap_add, Array, api: "HostConfig/CapAdd"
304
+ # --cap-drop=[] Drop Linux capabilities
305
+ cli_option :cap_drop, Array, api: "HostConfig/CapDrop"
306
+ # --cgroup-parent="" Optional parent cgroup for the container
307
+ cli_option :cgroup_parent, api: "HostConfig/CgroupParent"
308
+ # --cidfile="" Write the container ID to the file
309
+ cli_option :cidfile, api: "HostConfig/ContainerIDFile"
310
+ # --cpu-period=0 Limit CPU CFS (Completely Fair Scheduler) period
311
+ cli_option :cpu_period, api: "HostConfig/CpuShares"
312
+ # --cpu-quota=0 Limit CPU CFS (Completely Fair Scheduler) quota
313
+ cli_option :cpu_quota, api: "HostConfig/CpuQuota"
314
+ # --cpu-shares=0 CPU shares (relative weight)
315
+ cli_option :cpu_period, api: "HostConfig/CpuPeriod"
316
+ # --cpuset-cpus="" CPUs in which to allow execution (0-3, 0,1)
317
+ cli_option :cpuset_cpus, api: "HostConfig/CpusetCpus"
318
+ # --cpuset-mems="" Memory nodes (MEMs) in which to allow execution (0-3, 0,1)
319
+ cli_option :cpuset_mems, api: "HostConfig/CpusetMems"
320
+ # --device=[] Add a host device to the container
321
+ cli_option :device, "PathOnHost:PathInContainer", api: "HostConfig/Devices", aliases: :devices
322
+ # --device-read-bps=[] Limit read rate (bytes per second) from a device (e.g., --device-read-bps=/dev/sda:1mb)
323
+ cli_option :device_read_bps, "Path:Rate", api: "HostConfig/BlkioDeviceReadBps"
324
+ # --device-read-iops=[] Limit read rate (IO per second) from a device (e.g., --device-read-iops=/dev/sda:1000)
325
+ cli_option :device_read_iops, "Path:Rate", api: "HostConfig/BlkioDeviceReadIOps"
326
+ # --device-write-bps=[] Limit write rate (bytes per second) to a device (e.g., --device-write-bps=/dev/sda:1mb)
327
+ cli_option :device_write_bps, "Path:Rate", api: "HostConfig/BlkioDeviceWriteBps"
328
+ # --device-write-iops=[] Limit write rate (IO per second) to a device (e.g., --device-write-bps=/dev/sda:1000)
329
+ cli_option :device_write_iops, "Path:Rate", api: "HostConfig/BlkioDeviceWriteIOps"
330
+ # --dns=[] Set custom DNS servers
331
+ cli_option :dns, Array, api: "HostConfig/Dns"
332
+ # --dns-opt=[] Set custom DNS options
333
+ cli_option :dns_opt, Array, api: "HostConfig/DnsOptions", aliases: :dns_opts
334
+ # --dns-search=[] Set custom DNS search domains
335
+ cli_option :dns_search, Array, api: "HostConfig/DnsSearch"
336
+ # --entrypoint="" Overwrite the default ENTRYPOINT of the image
337
+ cli_option :entrypoint do |config, command|
338
+ command = Shellwords.split(command) if command.is_a?(String)
339
+ config["Entrypoint"] = command
340
+ end
341
+ # -e, --env=[] Set environment variables
342
+ cli_option :env, aliases: :e do |config, env|
343
+ if env.is_a?(Hash)
344
+ env = env.map { |k,v| "#{k}=#{v}" }
345
+ end
346
+ config["Env"] ||= []
347
+ config["Env"] += Array(env)
348
+ end
349
+ # --expose=[] Expose a port or a range of ports
350
+ cli_option :expose do |config, value|
351
+ config["ExposedPorts"] ||= {}
352
+ Array(value).each do |port|
353
+ parse_port(port).each do |host_ip, host_port, container_port|
354
+ config["ExposedPorts"][container_port] = {}
355
+ end
356
+ end
357
+ end
358
+ # --group-add=[] Add additional groups to run as
359
+ cli_option :group_add, Array, api: 'HostConfig/GroupAdd'
360
+ # -h, --hostname="" Container host name
361
+ cli_option :hostname, api: 'HostConfig/Hostname', aliases: :h
362
+ # -i, --interactive Keep STDIN open even if not attached
363
+ cli_option :interactive, :boolean, api: [ 'OpenStdin', 'AttachStdin' ]
364
+ # --ip="" Container IPv4 address (e.g. 172.30.100.104)
365
+ cli_option :ip do |config, value|
366
+ # Where this goes depends on the network! TODO doesn't work with `--net`
367
+ config["NetworkSettings"] ||= {}
368
+ network = config["NetworkMode"] || "default"
369
+ config["NetworkSettings"][network] ||= {}
370
+ config["NetworkSettings"][network]["IPAMConfig"] ||= {}
371
+ config["NetworkSettings"][network]["IPAMConfig"]["IPv4Address"] = value
372
+ end
373
+ # --ip6="" Container IPv6 address (e.g. 2001:db8::33)
374
+ cli_option :ip6 do |config, value|
375
+ # Where this goes depends on the network! TODO doesn't work with `--net`
376
+ config["NetworkSettings"] ||= {}
377
+ network = config["NetworkMode"] || "default"
378
+ config["NetworkSettings"][network] ||= {}
379
+ config["NetworkSettings"][network]["IPAMConfig"] ||= {}
380
+ config["NetworkSettings"][network]["IPAMConfig"]["IPv6Address"] = value
381
+ end
382
+ # --ipc="" IPC namespace to use
383
+ cli_option :ipc, api: 'HostConfig/IpcMode' do |config, value|
384
+ # TODO this should ONLY be set if security-opt isn't set at all.
385
+ config["HostConfig"]["SecurityOpt"] ||= [ "label:disable" ]
386
+ end
387
+ # --isolation="" Container isolation technology
388
+ cli_option :isolation, api: 'Isolation'
389
+ # --kernel-memory="" Kernel memory limit
390
+ cli_option :kernel_memory, Integer, api: 'KernelMemory'
391
+ # -l, --label=[] Set metadata on the container (e.g., --label=com.example.key=value)
392
+ cli_option :label, Array, api: "Labels", aliases: [ :l, :labels ]
393
+ # --link=[] Add link to another container
394
+ cli_option :link, Array, api: "HostConfig/Links", aliases: :links
395
+ # --log-driver="" Logging driver for container
396
+ cli_option :log_driver, api: "HostConfig/LogConfig/Type"
397
+ # --log-opt=[] Log driver specific options
398
+ cli_option :log_opt, aliases: :log_opts do |config, value|
399
+ config["HostConfig"] ||= {}
400
+ config["HostConfig"]["LogConfig"] ||= {}
401
+ config["HostConfig"]["LogConfig"]["Type"] ||= {}
402
+ Array(value).each do |keyval|
403
+ k,v = keyval.split("=", 2)
404
+ config["HostConfig"]["LogConfig"][k] = v
405
+ end
406
+ end
407
+ # --mac-address="" Container MAC address (e.g. 92:d0:c6:0a:29:33)
408
+ cli_option :mac_address, api: "MacAddress"
409
+ # -m, --memory="" Memory limit
410
+ cli_option :memory, Integer, api: "HostConfig/Memory", aliases: :m
411
+ # --memory-reservation="" Memory soft limit
412
+ cli_option :memory_reservation, Integer, api: "HostConfig/MemoryReservation"
413
+ # --memory-swap="" A positive integer equal to memory plus swap. Specify -1 to enable unlimited swap.
414
+ cli_option :memory_swap, Integer, api: "HostConfig/MemorySwap"
415
+ # --memory-swappiness="" Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.
416
+ cli_option :memory_swappiness, Integer, api: "HostConfig/MemorySwappiness"
417
+ # --net="bridge" Connect a container to a network
418
+ # 'bridge': create a network stack on the default Docker bridge
419
+ # 'none': no networking
420
+ # 'container:<name|id>': reuse another container's network stack
421
+ # 'host': use the Docker host network stack
422
+ # '<network-name>|<network-id>': connect to a user-defined network
423
+ cli_option :net do |config, value|
424
+ value = value.to_s
425
+ old_network = config["NetworkMode"] || "default"
426
+ config["NetworkMode"] = value
427
+ # If we already stored stuff in the default network, move it to the new network
428
+ if config["NetworkSettings"] && config["NetworkSettings"][old_network]
429
+ config["NetworkSettings"][value] = config["NetworkSettings"].delete(old_network)
430
+ end
431
+ end
432
+ # --net-alias=[] Add network-scoped alias for the container
433
+ cli_option :net_alias, aliases: :net_aliases do |config, value|
434
+ # Where this goes depends on the network! TODO doesn't work with `--net`
435
+ config["NetworkSettings"] ||= {}
436
+ network = config["NetworkMode"] || "default"
437
+ config["NetworkSettings"][network] ||= {}
438
+ config["NetworkSettings"][network]["Aliases"] ||= []
439
+ config["NetworkSettings"][network]["Aliases"] += Array(value)
440
+ end
441
+ # --oom-kill-disable Whether to disable OOM Killer for the container or not
442
+ cli_option :oom_kill_disable, :boolean, api: "HostConfig/OomKillDisable"
443
+ # --oom-score-adj=0 Tune the host's OOM preferences for containers (accepts -1000 to 1000)
444
+ cli_option :oom_score_adj, Integer, api: "HostConfig/OomScoreAdj"
445
+ # --pid="" PID namespace to use
446
+ cli_option :pid, api: "HostConfig/PidMode" do |config, value|
447
+ # TODO this should ONLY be set if security-opt isn't set at all.
448
+ config["HostConfig"]["SecurityOpt"] ||= [ "label:disable" ]
449
+ end
450
+ # --privileged Give extended privileges to this container
451
+ cli_option :privileged, :boolean, api: "HostConfig/Privileged"
452
+ # -p, --publish=[] Publish a container's port(s) to the host
453
+ cli_option :publish, aliases: [ :p, :ports ] do |config, value|
454
+ config["HostConfig"] ||= {}
455
+ config["HostConfig"]["PortBindings"] ||= {}
456
+ config["ExposedPorts"] ||= {}
457
+
458
+ Array(value).each do |port|
459
+ parse_port(port).each do |host_ip, host_port, container_port|
460
+ config["HostConfig"]["PortBindings"][container_port] ||= []
461
+ config["HostConfig"]["PortBindings"][container_port] << { "HostIp" => host_ip, "HostPort" => host_port }
462
+ config["ExposedPorts"][container_port] = {}
463
+ end
464
+ end
465
+ end
466
+ # -P, --publish-all Publish all exposed ports to random ports
467
+ cli_option :publish_all, :boolean, api: "HostConfig/PublishAllPorts", aliases: :P
468
+ # --read-only Mount the container's root filesystem as read only
469
+ cli_option :read_only, :boolean, api: "HostConfig/ReadonlyRootfs"
470
+ # --restart="no" Restart policy (no, on-failure[:max-retry], always, unless-stopped)
471
+ cli_option :restart do |config, value|
472
+ name, retries = value.split(':')
473
+ config["HostConfig"] ||= {}
474
+ config["HostConfig"]["RestartPolicy"] ||= {}
475
+ config["HostConfig"]["RestartPolicy"]["Name"] = name
476
+ if retries
477
+ config["HostConfig"]["RestartPolicy"]["MaximumRetryCount"] = retries
478
+ else
479
+ config["HostConfig"]["RestartPolicy"].delete("MaximumRetryCount")
480
+ end
481
+ end
482
+ # --rm Automatically remove the container when it exits
483
+ cli_option :rm, :boolean, api: "HostConfig/AutoRemove"
484
+ # --shm-size=[] Size of `/dev/shm`. The format is `<number><unit>`. `number` must be greater than `0`. Unit is optional and can be `b` (bytes), `k` (kilobytes), `m` (megabytes), or `g` (gigabytes). If you omit the unit, the system uses bytes. If you omit the size entirely, the system uses `64m`.
485
+ cli_option :shm_size, Integer, api: "HostConfig/ShmSize", aliases: :shm_sizes
486
+ # --security-opt=[] Security Options
487
+ cli_option :security_opt, Array, api: "HostConfig/SecurityOpt", aliases: :security_opts
488
+ # --stop-signal="SIGTERM" Signal to stop a container
489
+ cli_option :stop_signal, api: "StopSignal"
490
+ # -t, --tty Allocate a pseudo-TTY
491
+ cli_option :tty, :boolean, api: "Tty", aliases: :tty
492
+ # -u, --user="" Username or UID (format: <name|uid>[:<group|gid>])
493
+ cli_option :user, api: "User", aliases: :u
494
+ # --ulimit=[] Ulimit options
495
+ cli_option :ulimit, aliases: :ulimits do |config, value|
496
+ config["HostConfig"] ||= {}
497
+ config["HostConfig"]["Ulimits"] ||= []
498
+ value.each do |ulimit|
499
+ type, values = ulimit.split("=", 2)
500
+ soft, hard = values.split(":", 2)
501
+ config["HostConfig"]["Ulimits"] << { "Name" => type, "Soft" => soft, "Hard" => hard }
502
+ end
503
+ end
504
+ # --uts="" UTS namespace to use
505
+ cli_option :uts, api: "HostConfig/UTSMode"
506
+ # -v, --volume=[host-src:]container-dest[:<options>]
507
+ # Bind mount a volume. The comma-delimited
508
+ # `options` are [rw|ro], [z|Z], or
509
+ # [[r]shared|[r]slave|[r]private]. The
510
+ # 'host-src' is an absolute path or a name
511
+ # value.
512
+ cli_option :volume, aliases: [ :v, :volumes ] do |config, value|
513
+ # Things without : in them at all, are just volumes.
514
+ binds, volumes = Array(value).partition { |v| v.include?(':') }
515
+ config["HostConfig"] ||= {}
516
+ unless binds.empty?
517
+ config["HostConfig"]["Binds"] ||= []
518
+ config["HostConfig"]["Binds"] += binds
519
+ end
520
+ unless volumes.empty?
521
+ config["Volumes"] ||= []
522
+ config["Volumes"] += volumes
523
+ end
524
+ end
525
+ # --volume-driver="" Container's volume driver
526
+ cli_option :volume_driver, api: "HostConfig/VolumeDriver"
527
+ # -w, --workdir="" Working directory inside the container
528
+ cli_option :workdir, api: "WorkingDir", aliases: :w
529
+ # --volumes-from=[] Mount volumes from the specified container(s)
530
+ cli_option :volumes_from, Array, api: 'HostConfig/VolumesFrom'
531
+
532
+ # Not relevant to API or Chef:
533
+ # -d, --detach Run container in background and print container ID
534
+ # --detach-keys Specify the escape key sequence used to detach a container
535
+ # --disable-content-trust=true Skip image verification
536
+ # --env-file=[] Read in a file of environment variables
537
+ # --help Print usage
538
+ # --label-file=[] Read in a file of labels (EOL delimited)
539
+ # --name="" Assign a name to the container
540
+ # --sig-proxy=true Proxy received signals to the process
541
+
542
+ private
543
+
544
+ # Lifted from docker cookbook
545
+ def self.parse_port(v)
546
+ parts = v.to_s.split(':')
547
+ case parts.length
548
+ when 3
549
+ host_ip = parts[0]
550
+ host_port = parts[1]
551
+ container_port = parts[2]
552
+ when 2
553
+ host_ip = '0.0.0.0'
554
+ host_port = parts[0]
555
+ container_port = parts[1]
556
+ when 1
557
+ host_ip = ''
558
+ host_port = ''
559
+ container_port = parts[0]
560
+ end
561
+ port_range, protocol = container_port.split('/')
562
+ if port_range.include?('-')
563
+ port_range = container_port.split('-')
564
+ port_range.map!(&:to_i)
565
+ Chef::Log.fatal("FATAL: Invalid port range! #{container_port}") if port_range[0] > port_range[1]
566
+ port_range = (port_range[0]..port_range[1]).to_a
567
+ end
568
+ # qualify the port-binding protocol even when it is implicitly tcp #427.
569
+ protocol = 'tcp' if protocol.nil?
570
+ Array(port_range).map do |port|
571
+ [ host_ip, host_port, "#{port}/#{protocol}"]
572
+ end
573
+ end
574
+
575
+ def self.parse_int(value)
576
+ value = value.upcase
577
+ if value.end_with?("TB") || value.end_with?("T")
578
+ value.to_i * 1024*1024*1024*1024
579
+ elsif value.end_with?("GB") || value.end_with?("G")
580
+ value.to_i * 1024*1024*1024
581
+ elsif value.end_with?("MB") || value.end_with?("M")
582
+ value.to_i * 1024*1024
583
+ elsif value.end_with?("KB") || value.end_with?("K")
584
+ value.to_i * 1024
585
+ else
586
+ value.to_i
587
+ end
588
+ end
589
+
590
+ end
591
+ end
592
+ end
593
+ end