kitchen-docker 2.8.0 → 2.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +7 -0
- data/.gitignore +2 -0
- data/.kitchen.windows.yml +33 -0
- data/.kitchen.yml +26 -17
- data/.travis.yml +54 -14
- data/CHANGELOG.md +75 -60
- data/README.md +135 -20
- data/docker.ps1 +9 -0
- data/kitchen-docker.gemspec +4 -5
- data/lib/docker/version.rb +25 -0
- data/lib/kitchen/docker/container/linux.rb +136 -0
- data/lib/kitchen/docker/container/windows.rb +85 -0
- data/lib/kitchen/docker/container.rb +75 -0
- data/lib/kitchen/{driver → docker}/docker_version.rb +2 -5
- data/lib/kitchen/{driver/docker/erb.rb → docker/erb_context.rb} +2 -5
- data/lib/kitchen/docker/helpers/cli_helper.rb +172 -0
- data/lib/kitchen/docker/helpers/container_helper.rb +172 -0
- data/lib/kitchen/docker/helpers/dockerfile_helper.rb +136 -0
- data/lib/kitchen/docker/helpers/file_helper.rb +40 -0
- data/lib/kitchen/docker/helpers/image_helper.rb +76 -0
- data/lib/kitchen/docker/helpers/inspec_helper.rb +40 -0
- data/lib/kitchen/driver/docker.rb +77 -361
- data/lib/kitchen/transport/docker.rb +111 -0
- data/lib/train/docker.rb +125 -0
- data/test/Dockerfile +1 -1
- data/test/integration/default/serverspec/default_spec.rb +1 -1
- data/test/integration/default/serverspec/spec_helper.rb +21 -0
- data/test/integration/inspec/inspec_spec.rb +8 -2
- metadata +47 -10
@@ -0,0 +1,25 @@
|
|
1
|
+
#
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
3
|
+
# you may not use this file except in compliance with the License.
|
4
|
+
# You may obtain a copy of the License at
|
5
|
+
#
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
#
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11
|
+
# See the License for the specific language governing permissions and
|
12
|
+
# limitations under the License.
|
13
|
+
|
14
|
+
begin
|
15
|
+
require 'docker'
|
16
|
+
|
17
|
+
# Override API_VERSION constant in docker-api gem to use version 1.24 of the Docker API
|
18
|
+
# This override is for the docker-api gem to communicate to the Docker engine on Windows
|
19
|
+
module Docker
|
20
|
+
VERSION = '0.0.0'
|
21
|
+
API_VERSION = '1.24'
|
22
|
+
end
|
23
|
+
rescue LoadError => e
|
24
|
+
logger.debug("[Docker] docker-api gem not found for InSpec verifier. #{e}")
|
25
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
#
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
3
|
+
# you may not use this file except in compliance with the License.
|
4
|
+
# You may obtain a copy of the License at
|
5
|
+
#
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
#
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11
|
+
# See the License for the specific language governing permissions and
|
12
|
+
# limitations under the License.
|
13
|
+
|
14
|
+
require 'base64'
|
15
|
+
require 'openssl'
|
16
|
+
require 'securerandom'
|
17
|
+
require 'shellwords'
|
18
|
+
|
19
|
+
require_relative '../container'
|
20
|
+
require_relative '../helpers/dockerfile_helper'
|
21
|
+
|
22
|
+
module Kitchen
|
23
|
+
module Docker
|
24
|
+
class Container
|
25
|
+
class Linux < Kitchen::Docker::Container
|
26
|
+
include Kitchen::Docker::Helpers::DockerfileHelper
|
27
|
+
|
28
|
+
MUTEX_FOR_SSH_KEYS = Mutex.new
|
29
|
+
|
30
|
+
def initialize(config)
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
def create(state)
|
35
|
+
super
|
36
|
+
|
37
|
+
debug('Creating Linux container')
|
38
|
+
generate_keys
|
39
|
+
|
40
|
+
state[:ssh_key] = @config[:private_key]
|
41
|
+
state[:image_id] = build_image(state, dockerfile) unless state[:image_id]
|
42
|
+
state[:container_id] = run_container(state, 22) unless state[:container_id]
|
43
|
+
state[:hostname] = hostname(state)
|
44
|
+
state[:port] = container_ssh_port(state)
|
45
|
+
end
|
46
|
+
|
47
|
+
def execute(command)
|
48
|
+
# Create temp script file and upload files to container
|
49
|
+
debug("Executing command on Linux container (Platform: #{@config[:platform]})")
|
50
|
+
filename = "docker-#{::SecureRandom.uuid}.sh"
|
51
|
+
temp_file = "./.kitchen/temp/#{filename}"
|
52
|
+
create_temp_file(temp_file, command)
|
53
|
+
|
54
|
+
remote_path = @config[:temp_dir]
|
55
|
+
debug("Creating directory #{remote_path} on container")
|
56
|
+
create_dir_on_container(@config, remote_path)
|
57
|
+
|
58
|
+
debug("Uploading temp file #{temp_file} to #{remote_path} on container")
|
59
|
+
upload(temp_file, remote_path)
|
60
|
+
|
61
|
+
debug('Deleting temp file from local filesystem')
|
62
|
+
::File.delete(temp_file)
|
63
|
+
|
64
|
+
# Replace any environment variables used in the path and execute script file
|
65
|
+
debug("Executing temp script #{remote_path}/#{filename} on container")
|
66
|
+
remote_path = replace_env_variables(@config, remote_path)
|
67
|
+
|
68
|
+
container_exec(@config, "/bin/bash #{remote_path}/#{filename}")
|
69
|
+
rescue => e
|
70
|
+
raise "Failed to execute command on Linux container. #{e}"
|
71
|
+
end
|
72
|
+
|
73
|
+
protected
|
74
|
+
|
75
|
+
def generate_keys
|
76
|
+
MUTEX_FOR_SSH_KEYS.synchronize do
|
77
|
+
if !File.exist?(@config[:public_key]) || !File.exist?(@config[:private_key])
|
78
|
+
private_key = OpenSSL::PKey::RSA.new(2048)
|
79
|
+
blobbed_key = Base64.encode64(private_key.to_blob).gsub("\n", '')
|
80
|
+
public_key = "ssh-rsa #{blobbed_key} kitchen_docker_key"
|
81
|
+
File.open(@config[:private_key], 'w') do |file|
|
82
|
+
file.write(private_key)
|
83
|
+
file.chmod(0600)
|
84
|
+
end
|
85
|
+
File.open(@config[:public_key], 'w') do |file|
|
86
|
+
file.write(public_key)
|
87
|
+
file.chmod(0600)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_container_ssh_port(output)
|
94
|
+
_host, port = output.split(':')
|
95
|
+
port.to_i
|
96
|
+
rescue => e
|
97
|
+
raise ActionFailed, "Could not parse Docker port output for container SSH port. #{e}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def container_ssh_port(state)
|
101
|
+
return 22 if @config[:use_internal_docker_network]
|
102
|
+
|
103
|
+
output = docker_command("port #{state[:container_id]} 22/tcp")
|
104
|
+
parse_container_ssh_port(output)
|
105
|
+
rescue => e
|
106
|
+
raise ActionFailed, "Docker reports container has no ssh port mapped. #{e}"
|
107
|
+
end
|
108
|
+
|
109
|
+
def dockerfile
|
110
|
+
return dockerfile_template if @config[:dockerfile]
|
111
|
+
|
112
|
+
from = "FROM #{@config[:image]}"
|
113
|
+
platform = dockerfile_platform
|
114
|
+
username = @config[:username]
|
115
|
+
public_key = IO.read(@config[:public_key]).strip
|
116
|
+
homedir = username == 'root' ? '/root' : "/home/#{username}"
|
117
|
+
base = dockerfile_base_linux(username, homedir)
|
118
|
+
|
119
|
+
custom = ''
|
120
|
+
Array(@config[:provision_command]).each do |cmd|
|
121
|
+
custom << "RUN #{cmd}\n"
|
122
|
+
end
|
123
|
+
|
124
|
+
ssh_key = "RUN echo #{Shellwords.escape(public_key)} >> #{homedir}/.ssh/authorized_keys"
|
125
|
+
|
126
|
+
# Empty string to ensure the file ends with a newline.
|
127
|
+
output = [from, dockerfile_proxy_config, platform, base, custom, ssh_key, ''].join("\n")
|
128
|
+
debug('--- Start Dockerfile ---')
|
129
|
+
debug(output.strip)
|
130
|
+
debug('--- End Dockerfile ---')
|
131
|
+
output
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
#
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
3
|
+
# you may not use this file except in compliance with the License.
|
4
|
+
# You may obtain a copy of the License at
|
5
|
+
#
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
#
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11
|
+
# See the License for the specific language governing permissions and
|
12
|
+
# limitations under the License.
|
13
|
+
|
14
|
+
require 'securerandom'
|
15
|
+
|
16
|
+
require_relative '../container'
|
17
|
+
|
18
|
+
module Kitchen
|
19
|
+
module Docker
|
20
|
+
class Container
|
21
|
+
class Windows < Kitchen::Docker::Container
|
22
|
+
def initialize(config)
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def create(state)
|
27
|
+
super
|
28
|
+
|
29
|
+
debug('Creating Windows container')
|
30
|
+
state[:username] = @config[:username]
|
31
|
+
state[:image_id] = build_image(state, dockerfile) unless state[:image_id]
|
32
|
+
state[:container_id] = run_container(state) unless state[:container_id]
|
33
|
+
state[:hostname] = hostname(state)
|
34
|
+
end
|
35
|
+
|
36
|
+
def execute(command)
|
37
|
+
# Create temp script file and upload files to container
|
38
|
+
debug('Executing command on Windows container')
|
39
|
+
filename = "docker-#{::SecureRandom.uuid}.ps1"
|
40
|
+
temp_file = ".\\.kitchen\\temp\\#{filename}"
|
41
|
+
create_temp_file(temp_file, command)
|
42
|
+
|
43
|
+
remote_path = @config[:temp_dir].tr('/', '\\')
|
44
|
+
debug("Creating directory #{remote_path} on container")
|
45
|
+
create_dir_on_container(@config, remote_path)
|
46
|
+
|
47
|
+
debug("Uploading temp file #{temp_file} to #{remote_path} on container")
|
48
|
+
upload(temp_file, remote_path)
|
49
|
+
|
50
|
+
debug('Deleting temp file from local filesystem')
|
51
|
+
::File.delete(temp_file)
|
52
|
+
|
53
|
+
# Replace any environment variables used in the path and execute script file
|
54
|
+
debug("Executing temp script #{remote_path}\\#{filename} on container")
|
55
|
+
remote_path = replace_env_variables(@config, remote_path)
|
56
|
+
cmd = build_powershell_command("-File #{remote_path}\\#{filename}")
|
57
|
+
|
58
|
+
container_exec(@config, cmd)
|
59
|
+
rescue => e
|
60
|
+
raise "Failed to execute command on Windows container. #{e}"
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def dockerfile
|
66
|
+
raise ActionFailed, "Unknown platform '#{@config[:platform]}'" unless @config[:platform] == 'windows'
|
67
|
+
return dockerfile_template if @config[:dockerfile]
|
68
|
+
|
69
|
+
from = "FROM #{@config[:image]}"
|
70
|
+
|
71
|
+
custom = ''
|
72
|
+
Array(@config[:provision_command]).each do |cmd|
|
73
|
+
custom << "RUN #{cmd}\n"
|
74
|
+
end
|
75
|
+
|
76
|
+
output = [from, dockerfile_proxy_config, custom, ''].join("\n")
|
77
|
+
debug('--- Start Dockerfile ---')
|
78
|
+
debug(output.strip)
|
79
|
+
debug('--- End Dockerfile ---')
|
80
|
+
output
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
3
|
+
# you may not use this file except in compliance with the License.
|
4
|
+
# You may obtain a copy of the License at
|
5
|
+
#
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
#
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11
|
+
# See the License for the specific language governing permissions and
|
12
|
+
# limitations under the License.
|
13
|
+
|
14
|
+
require_relative 'helpers/cli_helper'
|
15
|
+
require_relative 'helpers/container_helper'
|
16
|
+
require_relative 'helpers/file_helper'
|
17
|
+
require_relative 'helpers/image_helper'
|
18
|
+
|
19
|
+
module Kitchen
|
20
|
+
module Docker
|
21
|
+
class Container
|
22
|
+
include Kitchen::Docker::Helpers::CliHelper
|
23
|
+
include Kitchen::Docker::Helpers::ContainerHelper
|
24
|
+
include Kitchen::Docker::Helpers::FileHelper
|
25
|
+
include Kitchen::Docker::Helpers::ImageHelper
|
26
|
+
|
27
|
+
def initialize(config)
|
28
|
+
@config = config
|
29
|
+
end
|
30
|
+
|
31
|
+
def create(state)
|
32
|
+
if container_exists?(state)
|
33
|
+
info("Container ID #{state[:container_id]} already exists.")
|
34
|
+
elsif !container_exists?(state) && state[:container_id]
|
35
|
+
raise ActionFailed, "Container ID #{state[:container_id]} was found in the kitchen state data, "\
|
36
|
+
'but the container does not exist.'
|
37
|
+
end
|
38
|
+
|
39
|
+
state[:username] = @config[:username]
|
40
|
+
end
|
41
|
+
|
42
|
+
def destroy(state)
|
43
|
+
info("[Docker] Destroying Docker container #{state[:container_id]}") if state[:container_id]
|
44
|
+
remove_container(state) if container_exists?(state)
|
45
|
+
|
46
|
+
if @config[:remove_images] && state[:image_id]
|
47
|
+
remove_image(state) if image_exists?(state)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def hostname(state)
|
52
|
+
hostname = 'localhost'
|
53
|
+
|
54
|
+
if remote_socket?
|
55
|
+
hostname = socket_uri.host
|
56
|
+
elsif @config[:use_internal_docker_network]
|
57
|
+
hostname = container_ip_address(state)
|
58
|
+
end
|
59
|
+
|
60
|
+
hostname
|
61
|
+
end
|
62
|
+
|
63
|
+
def upload(locals, remote)
|
64
|
+
files = locals
|
65
|
+
files = Array(locals) unless locals.is_a?(Array)
|
66
|
+
|
67
|
+
files.each do |file|
|
68
|
+
copy_file_to_container(@config, file, remote)
|
69
|
+
end
|
70
|
+
|
71
|
+
files
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
#
|
3
2
|
# Copyright (C) 2014, Sean Porter
|
4
3
|
#
|
@@ -15,10 +14,8 @@
|
|
15
14
|
# limitations under the License.
|
16
15
|
|
17
16
|
module Kitchen
|
18
|
-
|
19
|
-
module Driver
|
20
|
-
|
17
|
+
module Docker
|
21
18
|
# Version string for Docker Kitchen driver
|
22
|
-
DOCKER_VERSION = "2.
|
19
|
+
DOCKER_VERSION = "2.12.0"
|
23
20
|
end
|
24
21
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
#
|
3
2
|
# Copyright (C) 2014, Sean Porter
|
4
3
|
#
|
@@ -17,10 +16,8 @@
|
|
17
16
|
require 'erb'
|
18
17
|
|
19
18
|
module Kitchen
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
class DockerERBContext
|
19
|
+
module Docker
|
20
|
+
class ERBContext
|
24
21
|
def initialize(config={})
|
25
22
|
config.each do |key, value|
|
26
23
|
instance_variable_set('@' + key.to_s, value)
|
@@ -0,0 +1,172 @@
|
|
1
|
+
#
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
3
|
+
# you may not use this file except in compliance with the License.
|
4
|
+
# You may obtain a copy of the License at
|
5
|
+
#
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
#
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11
|
+
# See the License for the specific language governing permissions and
|
12
|
+
# limitations under the License.
|
13
|
+
|
14
|
+
require 'kitchen'
|
15
|
+
require 'kitchen/configurable'
|
16
|
+
require 'kitchen/logging'
|
17
|
+
require 'kitchen/shell_out'
|
18
|
+
|
19
|
+
module Kitchen
|
20
|
+
module Docker
|
21
|
+
module Helpers
|
22
|
+
module CliHelper
|
23
|
+
include Configurable
|
24
|
+
include Logging
|
25
|
+
include ShellOut
|
26
|
+
|
27
|
+
def docker_command(cmd, options={})
|
28
|
+
docker = config[:binary].dup
|
29
|
+
docker << " -H #{config[:socket]}" if config[:socket]
|
30
|
+
docker << ' --tls' if config[:tls]
|
31
|
+
docker << ' --tlsverify' if config[:tls_verify]
|
32
|
+
docker << " --tlscacert=#{config[:tls_cacert]}" if config[:tls_cacert]
|
33
|
+
docker << " --tlscert=#{config[:tls_cert]}" if config[:tls_cert]
|
34
|
+
docker << " --tlskey=#{config[:tls_key]}" if config[:tls_key]
|
35
|
+
logger.debug("docker_command: #{docker} #{cmd} shell_opts: #{docker_shell_opts(options)}")
|
36
|
+
run_command("#{docker} #{cmd}", docker_shell_opts(options))
|
37
|
+
end
|
38
|
+
|
39
|
+
# Copied from kitchen because we need stderr
|
40
|
+
def run_command(cmd, options = {})
|
41
|
+
if options.fetch(:use_sudo, false)
|
42
|
+
cmd = "#{options.fetch(:sudo_command, "sudo -E")} #{cmd}"
|
43
|
+
end
|
44
|
+
subject = "[#{options.fetch(:log_subject, "local")} command]"
|
45
|
+
|
46
|
+
debug("#{subject} BEGIN (#{cmd})")
|
47
|
+
sh = Mixlib::ShellOut.new(cmd, shell_opts(options))
|
48
|
+
sh.run_command
|
49
|
+
debug("#{subject} END #{Util.duration(sh.execution_time)}")
|
50
|
+
sh.error!
|
51
|
+
sh.stdout + sh.stderr
|
52
|
+
rescue Mixlib::ShellOut::ShellCommandFailed => ex
|
53
|
+
raise ShellCommandFailed, ex.message
|
54
|
+
rescue Exception => error # rubocop:disable Lint/RescueException
|
55
|
+
error.extend(Kitchen::Error)
|
56
|
+
raise
|
57
|
+
end
|
58
|
+
|
59
|
+
def build_run_command(image_id, transport_port = nil)
|
60
|
+
cmd = 'run -d'
|
61
|
+
cmd << ' -i' if config[:interactive]
|
62
|
+
cmd << ' -t' if config[:tty]
|
63
|
+
cmd << build_env_variable_args(config[:env_variables]) if config[:env_variables]
|
64
|
+
cmd << " -p #{transport_port}" unless transport_port.nil?
|
65
|
+
Array(config[:forward]).each { |port| cmd << " -p #{port}" }
|
66
|
+
Array(config[:dns]).each { |dns| cmd << " --dns #{dns}" }
|
67
|
+
Array(config[:add_host]).each { |host, ip| cmd << " --add-host=#{host}:#{ip}" }
|
68
|
+
Array(config[:volume]).each { |volume| cmd << " -v #{volume}" }
|
69
|
+
Array(config[:volumes_from]).each { |container| cmd << " --volumes-from #{container}" }
|
70
|
+
Array(config[:links]).each { |link| cmd << " --link #{link}" }
|
71
|
+
Array(config[:devices]).each { |device| cmd << " --device #{device}" }
|
72
|
+
Array(config[:mount]).each {|mount| cmd << " --mount #{mount}"}
|
73
|
+
Array(config[:tmpfs]).each {|tmpfs| cmd << " --tmpfs #{tmpfs}"}
|
74
|
+
cmd << " --name #{config[:instance_name]}" if config[:instance_name]
|
75
|
+
cmd << ' -P' if config[:publish_all]
|
76
|
+
cmd << " -h #{config[:hostname]}" if config[:hostname]
|
77
|
+
cmd << " -m #{config[:memory]}" if config[:memory]
|
78
|
+
cmd << " -c #{config[:cpu]}" if config[:cpu]
|
79
|
+
cmd << " --gpus #{config[:gpus]}" if config[:gpus]
|
80
|
+
cmd << " -e http_proxy=#{config[:http_proxy]}" if config[:http_proxy]
|
81
|
+
cmd << " -e https_proxy=#{config[:https_proxy]}" if config[:https_proxy]
|
82
|
+
cmd << ' --privileged' if config[:privileged]
|
83
|
+
cmd << " --isolation #{config[:isolation]}" if config[:isolation]
|
84
|
+
Array(config[:cap_add]).each { |cap| cmd << " --cap-add=#{cap}"} if config[:cap_add]
|
85
|
+
Array(config[:cap_drop]).each { |cap| cmd << " --cap-drop=#{cap}"} if config[:cap_drop]
|
86
|
+
Array(config[:security_opt]).each { |opt| cmd << " --security-opt=#{opt}"} if config[:security_opt]
|
87
|
+
cmd << " --platform=#{config[:docker_platform]}" if config[:docker_platform]
|
88
|
+
extra_run_options = config_to_options(config[:run_options])
|
89
|
+
cmd << " #{extra_run_options}" unless extra_run_options.empty?
|
90
|
+
cmd << " #{image_id} #{config[:run_command]}"
|
91
|
+
logger.debug("build_run_command: #{cmd}")
|
92
|
+
cmd
|
93
|
+
end
|
94
|
+
|
95
|
+
def build_exec_command(state, command)
|
96
|
+
cmd = 'exec'
|
97
|
+
cmd << ' -d' if config[:detach]
|
98
|
+
cmd << build_env_variable_args(config[:env_variables]) if config[:env_variables]
|
99
|
+
cmd << ' --privileged' if config[:privileged]
|
100
|
+
cmd << ' -t' if config[:tty]
|
101
|
+
cmd << ' -i' if config[:interactive]
|
102
|
+
cmd << " -u #{config[:username]}" if config[:username]
|
103
|
+
cmd << " -w #{config[:working_dir]}" if config[:working_dir]
|
104
|
+
cmd << " #{state[:container_id]}"
|
105
|
+
cmd << " #{command}"
|
106
|
+
logger.debug("build_exec_command: #{cmd}")
|
107
|
+
cmd
|
108
|
+
end
|
109
|
+
|
110
|
+
def build_copy_command(local_file, remote_file, opts = {})
|
111
|
+
cmd = 'cp'
|
112
|
+
cmd << ' -a' if opts[:archive]
|
113
|
+
cmd << " #{local_file} #{remote_file}"
|
114
|
+
cmd
|
115
|
+
end
|
116
|
+
|
117
|
+
def build_powershell_command(args)
|
118
|
+
cmd = 'powershell -ExecutionPolicy Bypass -NoLogo '
|
119
|
+
cmd << args
|
120
|
+
logger.debug("build_powershell_command: #{cmd}")
|
121
|
+
cmd
|
122
|
+
end
|
123
|
+
|
124
|
+
def build_env_variable_args(vars)
|
125
|
+
raise ActionFailed, 'Environment variables are not of a Hash type' unless vars.is_a?(Hash)
|
126
|
+
|
127
|
+
args = ''
|
128
|
+
vars.each do |k, v|
|
129
|
+
args << " -e #{k.to_s.strip}=\"#{v.to_s.strip}\""
|
130
|
+
end
|
131
|
+
|
132
|
+
args
|
133
|
+
end
|
134
|
+
|
135
|
+
def dev_null
|
136
|
+
case RbConfig::CONFIG['host_os']
|
137
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
138
|
+
'NUL'
|
139
|
+
else
|
140
|
+
'/dev/null'
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def docker_shell_opts(options = {})
|
145
|
+
options[:live_stream] = nil if options[:suppress_output]
|
146
|
+
options.delete(:suppress_output)
|
147
|
+
|
148
|
+
options
|
149
|
+
end
|
150
|
+
|
151
|
+
# Convert the config input for `:build_options` or `:run_options` in to a
|
152
|
+
# command line string for use with Docker.
|
153
|
+
#
|
154
|
+
# @since 2.5.0
|
155
|
+
# @param config [nil, String, Array, Hash] Config data to convert.
|
156
|
+
# @return [String]
|
157
|
+
def config_to_options(config)
|
158
|
+
case config
|
159
|
+
when nil
|
160
|
+
''
|
161
|
+
when String
|
162
|
+
config
|
163
|
+
when Array
|
164
|
+
config.map { |c| config_to_options(c) }.join(' ')
|
165
|
+
when Hash
|
166
|
+
config.map { |k, v| Array(v).map { |c| "--#{k}=#{Shellwords.escape(c)}" }.join(' ') }.join(' ')
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|