kitchen-docker 2.14.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,172 +1,184 @@
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
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
+ # rubocop:disable Metrics/ModuleLength, Style/Documentation
23
+ module CliHelper
24
+ include Configurable
25
+ include Logging
26
+ include ShellOut
27
+
28
+ # rubocop:disable Metrics/AbcSize
29
+ def docker_command(cmd, options={})
30
+ docker = config[:binary].dup
31
+ docker << " -H #{config[:socket]}" if config[:socket]
32
+ docker << ' --tls' if config[:tls]
33
+ docker << ' --tlsverify' if config[:tls_verify]
34
+ docker << " --tlscacert=#{config[:tls_cacert]}" if config[:tls_cacert]
35
+ docker << " --tlscert=#{config[:tls_cert]}" if config[:tls_cert]
36
+ docker << " --tlskey=#{config[:tls_key]}" if config[:tls_key]
37
+ logger.debug("docker_command: #{docker} #{cmd} shell_opts: #{docker_shell_opts(options)}")
38
+ run_command("#{docker} #{cmd}", docker_shell_opts(options))
39
+ end
40
+ # rubocop:enable Metrics/AbcSize
41
+
42
+ # Copied from kitchen because we need stderr
43
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
44
+ def run_command(cmd, options = {})
45
+ if options.fetch(:use_sudo, false)
46
+ cmd = "#{options.fetch(:sudo_command, "sudo -E")} #{cmd}"
47
+ end
48
+ subject = "[#{options.fetch(:log_subject, "local")} command]"
49
+
50
+ debug("#{subject} BEGIN (#{cmd})")
51
+ sh = Mixlib::ShellOut.new(cmd, shell_opts(options))
52
+ sh.run_command
53
+ debug("#{subject} END #{Util.duration(sh.execution_time)}")
54
+ sh.error!
55
+ sh.stdout + sh.stderr
56
+ rescue Mixlib::ShellOut::ShellCommandFailed => ex
57
+ raise ShellCommandFailed, ex.message
58
+ rescue Exception => error # rubocop:disable Lint/RescueException
59
+ error.extend(Kitchen::Error)
60
+ raise
61
+ end
62
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
63
+
64
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/AbcSize
65
+ def build_run_command(image_id, transport_port = nil)
66
+ cmd = 'run -d'
67
+ cmd << ' -i' if config[:interactive]
68
+ cmd << ' -t' if config[:tty]
69
+ cmd << build_env_variable_args(config[:env_variables]) if config[:env_variables]
70
+ cmd << " -p #{transport_port}" unless transport_port.nil?
71
+ Array(config[:forward]).each { |port| cmd << " -p #{port}" }
72
+ Array(config[:dns]).each { |dns| cmd << " --dns #{dns}" }
73
+ Array(config[:add_host]).each { |host, ip| cmd << " --add-host=#{host}:#{ip}" }
74
+ Array(config[:volume]).each { |volume| cmd << " -v #{volume}" }
75
+ Array(config[:volumes_from]).each { |container| cmd << " --volumes-from #{container}" }
76
+ Array(config[:links]).each { |link| cmd << " --link #{link}" }
77
+ Array(config[:devices]).each { |device| cmd << " --device #{device}" }
78
+ Array(config[:mount]).each {|mount| cmd << " --mount #{mount}"}
79
+ Array(config[:tmpfs]).each {|tmpfs| cmd << " --tmpfs #{tmpfs}"}
80
+ cmd << " --name #{config[:instance_name]}" if config[:instance_name]
81
+ cmd << ' -P' if config[:publish_all]
82
+ cmd << " -h #{config[:hostname]}" if config[:hostname]
83
+ cmd << " -m #{config[:memory]}" if config[:memory]
84
+ cmd << " -c #{config[:cpu]}" if config[:cpu]
85
+ cmd << " --gpus #{config[:gpus]}" if config[:gpus]
86
+ cmd << " -e http_proxy=#{config[:http_proxy]}" if config[:http_proxy]
87
+ cmd << " -e https_proxy=#{config[:https_proxy]}" if config[:https_proxy]
88
+ cmd << ' --privileged' if config[:privileged]
89
+ cmd << " --isolation #{config[:isolation]}" if config[:isolation]
90
+ Array(config[:cap_add]).each { |cap| cmd << " --cap-add=#{cap}"} if config[:cap_add]
91
+ Array(config[:cap_drop]).each { |cap| cmd << " --cap-drop=#{cap}"} if config[:cap_drop]
92
+ Array(config[:security_opt]).each { |opt| cmd << " --security-opt=#{opt}"} if config[:security_opt]
93
+ cmd << " --platform=#{config[:docker_platform]}" if config[:docker_platform]
94
+ extra_run_options = config_to_options(config[:run_options])
95
+ cmd << " #{extra_run_options}" unless extra_run_options.empty?
96
+ cmd << " #{image_id} #{config[:run_command]}"
97
+ logger.debug("build_run_command: #{cmd}")
98
+ cmd
99
+ end
100
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/AbcSize
101
+
102
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/AbcSize
103
+ def build_exec_command(state, command)
104
+ cmd = 'exec'
105
+ cmd << ' -d' if config[:detach]
106
+ cmd << build_env_variable_args(config[:env_variables]) if config[:env_variables]
107
+ cmd << ' --privileged' if config[:privileged]
108
+ cmd << ' -t' if config[:tty]
109
+ cmd << ' -i' if config[:interactive]
110
+ cmd << " -u #{config[:username]}" if config[:username]
111
+ cmd << " -w #{config[:working_dir]}" if config[:working_dir]
112
+ cmd << " #{state[:container_id]}"
113
+ cmd << " #{command}"
114
+ logger.debug("build_exec_command: #{cmd}")
115
+ cmd
116
+ end
117
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/AbcSize
118
+
119
+ def build_copy_command(local_file, remote_file, opts = {})
120
+ cmd = 'cp'
121
+ cmd << ' -a' if opts[:archive]
122
+ cmd << " #{local_file} #{remote_file}"
123
+ cmd
124
+ end
125
+
126
+ def build_powershell_command(args)
127
+ cmd = 'powershell -ExecutionPolicy Bypass -NoLogo '
128
+ cmd << args
129
+ logger.debug("build_powershell_command: #{cmd}")
130
+ cmd
131
+ end
132
+
133
+ def build_env_variable_args(vars)
134
+ raise ActionFailed, 'Environment variables are not of a Hash type' unless vars.is_a?(Hash)
135
+
136
+ args = ''
137
+ vars.each do |k, v|
138
+ args << " -e #{k.to_s.strip}=\"#{v.to_s.strip}\""
139
+ end
140
+
141
+ args
142
+ end
143
+
144
+ def dev_null
145
+ case RbConfig::CONFIG['host_os']
146
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
147
+ 'NUL'
148
+ else
149
+ '/dev/null'
150
+ end
151
+ end
152
+
153
+ def docker_shell_opts(options = {})
154
+ options[:live_stream] = nil if options[:suppress_output]
155
+ options.delete(:suppress_output)
156
+
157
+ options
158
+ end
159
+
160
+ # Convert the config input for `:build_options` or `:run_options` in to a
161
+ # command line string for use with Docker.
162
+ #
163
+ # @since 2.5.0
164
+ # @param config [nil, String, Array, Hash] Config data to convert.
165
+ # @return [String]
166
+ # rubocop:disable Metrics/CyclomaticComplexity
167
+ def config_to_options(config)
168
+ case config
169
+ when nil
170
+ ''
171
+ when String
172
+ config
173
+ when Array
174
+ config.map { |c| config_to_options(c) }.join(' ')
175
+ when Hash
176
+ config.map { |k, v| Array(v).map { |c| "--#{k}=#{Shellwords.escape(c)}" }.join(' ') }.join(' ')
177
+ end
178
+ end
179
+ # rubocop:enable Metrics/CyclomaticComplexity
180
+ end
181
+ # rubocop:enable Metrics/ModuleLength, Style/Documentation
182
+ end
183
+ end
184
+ end