bolt 3.0.0 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bolt might be problematic. Click here for more details.

Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +13 -11
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +24 -0
  4. data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -1
  5. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +20 -2
  6. data/bolt-modules/boltlib/lib/puppet/functions/run_container.rb +162 -0
  7. data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +2 -2
  8. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +44 -5
  9. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
  10. data/bolt-modules/boltlib/types/planresult.pp +1 -0
  11. data/bolt-modules/file/lib/puppet/functions/file/read.rb +3 -2
  12. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +20 -2
  13. data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
  14. data/lib/bolt/apply_result.rb +1 -1
  15. data/lib/bolt/bolt_option_parser.rb +6 -3
  16. data/lib/bolt/cli.rb +96 -16
  17. data/lib/bolt/config.rb +4 -0
  18. data/lib/bolt/config/options.rb +21 -3
  19. data/lib/bolt/config/transport/lxd.rb +23 -0
  20. data/lib/bolt/config/transport/options.rb +8 -1
  21. data/lib/bolt/container_result.rb +105 -0
  22. data/lib/bolt/error.rb +15 -0
  23. data/lib/bolt/executor.rb +22 -7
  24. data/lib/bolt/inventory/options.rb +9 -0
  25. data/lib/bolt/inventory/target.rb +16 -0
  26. data/lib/bolt/logger.rb +8 -0
  27. data/lib/bolt/module_installer.rb +2 -2
  28. data/lib/bolt/module_installer/puppetfile.rb +2 -2
  29. data/lib/bolt/module_installer/specs/forge_spec.rb +2 -2
  30. data/lib/bolt/module_installer/specs/git_spec.rb +2 -2
  31. data/lib/bolt/node/output.rb +14 -4
  32. data/lib/bolt/outputter/human.rb +106 -23
  33. data/lib/bolt/outputter/logger.rb +17 -0
  34. data/lib/bolt/pal.rb +25 -4
  35. data/lib/bolt/pal/yaml_plan.rb +1 -2
  36. data/lib/bolt/pal/yaml_plan/evaluator.rb +5 -141
  37. data/lib/bolt/pal/yaml_plan/step.rb +91 -31
  38. data/lib/bolt/pal/yaml_plan/step/command.rb +21 -13
  39. data/lib/bolt/pal/yaml_plan/step/download.rb +15 -16
  40. data/lib/bolt/pal/yaml_plan/step/eval.rb +11 -11
  41. data/lib/bolt/pal/yaml_plan/step/message.rb +13 -4
  42. data/lib/bolt/pal/yaml_plan/step/plan.rb +19 -15
  43. data/lib/bolt/pal/yaml_plan/step/resources.rb +82 -21
  44. data/lib/bolt/pal/yaml_plan/step/script.rb +36 -17
  45. data/lib/bolt/pal/yaml_plan/step/task.rb +19 -16
  46. data/lib/bolt/pal/yaml_plan/step/upload.rb +16 -17
  47. data/lib/bolt/pal/yaml_plan/transpiler.rb +3 -3
  48. data/lib/bolt/plan_creator.rb +1 -1
  49. data/lib/bolt/project_manager.rb +1 -1
  50. data/lib/bolt/project_manager/module_migrator.rb +1 -1
  51. data/lib/bolt/result.rb +11 -15
  52. data/lib/bolt/shell.rb +16 -0
  53. data/lib/bolt/shell/bash.rb +61 -31
  54. data/lib/bolt/shell/bash/tmpdir.rb +2 -2
  55. data/lib/bolt/shell/powershell.rb +34 -12
  56. data/lib/bolt/shell/powershell/snippets.rb +30 -3
  57. data/lib/bolt/task.rb +1 -1
  58. data/lib/bolt/transport/base.rb +0 -9
  59. data/lib/bolt/transport/docker.rb +1 -125
  60. data/lib/bolt/transport/docker/connection.rb +77 -167
  61. data/lib/bolt/transport/lxd.rb +26 -0
  62. data/lib/bolt/transport/lxd/connection.rb +99 -0
  63. data/lib/bolt/transport/orch.rb +13 -5
  64. data/lib/bolt/transport/ssh/connection.rb +1 -1
  65. data/lib/bolt/transport/winrm/connection.rb +1 -1
  66. data/lib/bolt/util.rb +31 -0
  67. data/lib/bolt/version.rb +1 -1
  68. data/lib/bolt_server/transport_app.rb +61 -33
  69. data/lib/bolt_spec/bolt_context.rb +9 -4
  70. data/lib/bolt_spec/plans.rb +1 -109
  71. data/lib/bolt_spec/plans/action_stubs.rb +1 -1
  72. data/lib/bolt_spec/plans/action_stubs/command_stub.rb +8 -1
  73. data/lib/bolt_spec/plans/action_stubs/script_stub.rb +8 -1
  74. data/lib/bolt_spec/plans/mock_executor.rb +90 -7
  75. data/modules/puppet_connect/plans/test_input_data.pp +65 -7
  76. metadata +9 -2
@@ -55,18 +55,45 @@ module Bolt
55
55
  }
56
56
  #{build_arg_list}
57
57
 
58
+ switch -regex ( Get-ExecutionPolicy )
59
+ {
60
+ '^AllSigned'
61
+ {
62
+ if ((Get-AuthenticodeSignature -File "#{script_path}").Status -ne 'Valid') {
63
+ $Host.UI.WriteErrorLine("Error: Target host Powershell ExecutionPolicy is set to ${_} and script '#{script_path}' does not contain a valid signature.")
64
+ exit 1;
65
+ }
66
+ }
67
+ '^Restricted'
68
+ {
69
+ $Host.UI.WriteErrorLine("Error: Target host Powershell ExecutionPolicy is set to ${_} which denies running any scripts on the target.")
70
+ exit 1;
71
+ }
72
+ }
73
+
74
+ if([string]::IsNullOrEmpty($invokeArgs.ScriptBlock)){
75
+ $Host.UI.WriteErrorLine("Error: Failed to obtain scriptblock from '#{script_path}'. Running scripts might be disabled on this system. For more information, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170");
76
+ exit 1;
77
+ }
78
+
58
79
  try
59
80
  {
60
81
  Invoke-Command @invokeArgs
61
82
  }
62
83
  catch
63
84
  {
64
- Write-Error $_.Exception
65
- exit 1
85
+ $Host.UI.WriteErrorLine("[$($_.FullyQualifiedErrorId)] Exception $($_.InvocationInfo.PositionMessage).`n$($_.Exception.Message)");
86
+ exit 1;
66
87
  }
67
88
  PS
68
89
  end
69
90
 
91
+ def append_ps_module_path(directory)
92
+ <<~PS
93
+ $env:PSModulePath += ";#{directory}"
94
+ PS
95
+ end
96
+
70
97
  def ps_task(path, arguments)
71
98
  <<~PS
72
99
  $private:tempArgs = Get-ContentAsJson (
@@ -138,7 +165,7 @@ module Bolt
138
165
  [Parameter(Mandatory = $true)] $Text,
139
166
  [Parameter(Mandatory = $false)] [Text.Encoding] $Encoding = [Text.Encoding]::UTF8
140
167
  )
141
-
168
+
142
169
  $Text | ConvertFrom-Json | ConvertFrom-PSCustomObject
143
170
  }
144
171
  PS
data/lib/bolt/task.rb CHANGED
@@ -148,7 +148,7 @@ module Bolt
148
148
 
149
149
  if unknown_keys.any?
150
150
  msg = "Metadata for task '#{@name}' contains unknown keys: #{unknown_keys.join(', ')}."
151
- msg += " This could be a typo in the task metadata or may result in incorrect behavior."
151
+ msg += " This could be a typo in the task metadata or might result in incorrect behavior."
152
152
  Bolt::Logger.warn("unknown_task_metadata_keys", msg)
153
153
  end
154
154
  end
@@ -74,15 +74,6 @@ module Bolt
74
74
  interpreters[Pathname(executable).extname] if interpreters
75
75
  end
76
76
 
77
- # Transform a parameter map to an environment variable map, with parameter names prefixed
78
- # with 'PT_' and values transformed to JSON unless they're strings.
79
- def envify_params(params)
80
- params.each_with_object({}) do |(k, v), h|
81
- v = v.to_json unless v.is_a?(String)
82
- h["PT_#{k}"] = v
83
- end
84
- end
85
-
86
77
  # Raises an error if more than one target was given in the batch.
87
78
  #
88
79
  # The default implementations of batch_* strictly assume the transport is
@@ -6,7 +6,7 @@ require 'bolt/transport/base'
6
6
 
7
7
  module Bolt
8
8
  module Transport
9
- class Docker < Base
9
+ class Docker < Simple
10
10
  def provided_features
11
11
  ['shell']
12
12
  end
@@ -16,130 +16,6 @@ module Bolt
16
16
  conn.connect
17
17
  yield conn
18
18
  end
19
-
20
- def upload(target, source, destination, _options = {})
21
- with_connection(target) do |conn|
22
- conn.with_remote_tmpdir do |dir|
23
- basename = File.basename(source)
24
- tmpfile = "#{dir}/#{basename}"
25
- if File.directory?(source)
26
- conn.write_remote_directory(source, tmpfile)
27
- else
28
- conn.write_remote_file(source, tmpfile)
29
- end
30
-
31
- _, stderr, exitcode = conn.execute('mv', tmpfile, destination, {})
32
- if exitcode != 0
33
- message = "Could not move temporary file '#{tmpfile}' to #{destination}: #{stderr}"
34
- raise Bolt::Node::FileError.new(message, 'MV_ERROR')
35
- end
36
- end
37
- Bolt::Result.for_upload(target, source, destination)
38
- end
39
- end
40
-
41
- def download(target, source, destination, _options = {})
42
- with_connection(target) do |conn|
43
- download = File.join(destination, Bolt::Util.unix_basename(source))
44
- conn.download_remote_content(source, destination)
45
- Bolt::Result.for_download(target, source, destination, download)
46
- end
47
- end
48
-
49
- def run_command(target, command, options = {}, position = [])
50
- execute_options = {}
51
- execute_options[:tty] = target.options['tty']
52
- execute_options[:environment] = options[:env_vars]
53
-
54
- if target.options['shell-command'] && !target.options['shell-command'].empty?
55
- # escape any double quotes in command
56
- command = command.gsub('"', '\"')
57
- command = "#{target.options['shell-command']} \" #{command}\""
58
- end
59
- with_connection(target) do |conn|
60
- stdout, stderr, exitcode = conn.execute(*Shellwords.split(command), execute_options)
61
- Bolt::Result.for_command(target,
62
- stdout,
63
- stderr,
64
- exitcode,
65
- 'command',
66
- command,
67
- position)
68
- end
69
- end
70
-
71
- def run_script(target, script, arguments, options = {}, position = [])
72
- # unpack any Sensitive data
73
- arguments = unwrap_sensitive_args(arguments)
74
- execute_options = {}
75
- execute_options[:environment] = options[:env_vars]
76
-
77
- with_connection(target) do |conn|
78
- conn.with_remote_tmpdir do |dir|
79
- remote_path = conn.write_remote_executable(dir, script)
80
- stdout, stderr, exitcode = conn.execute(remote_path, *arguments, execute_options)
81
- Bolt::Result.for_command(target,
82
- stdout,
83
- stderr,
84
- exitcode,
85
- 'script',
86
- script,
87
- position)
88
- end
89
- end
90
- end
91
-
92
- def run_task(target, task, arguments, _options = {}, position = [])
93
- implementation = task.select_implementation(target, provided_features)
94
- executable = implementation['path']
95
- input_method = implementation['input_method']
96
- extra_files = implementation['files']
97
- input_method ||= 'both'
98
-
99
- # unpack any Sensitive data
100
- arguments = unwrap_sensitive_args(arguments)
101
- with_connection(target) do |conn|
102
- execute_options = {}
103
- execute_options[:interpreter] = select_interpreter(executable, target.options['interpreters'])
104
- conn.with_remote_tmpdir do |dir|
105
- if extra_files.empty?
106
- task_dir = dir
107
- else
108
- # TODO: optimize upload of directories
109
- arguments['_installdir'] = dir
110
- task_dir = File.join(dir, task.tasks_dir)
111
- conn.mkdirs([task_dir] + extra_files.map { |file| File.join(dir, File.dirname(file['name'])) })
112
- extra_files.each do |file|
113
- conn.write_remote_file(file['path'], File.join(dir, file['name']))
114
- end
115
- end
116
-
117
- remote_task_path = conn.write_remote_executable(task_dir, executable)
118
-
119
- if Bolt::Task::STDIN_METHODS.include?(input_method)
120
- execute_options[:stdin] = StringIO.new(JSON.dump(arguments))
121
- end
122
-
123
- if Bolt::Task::ENVIRONMENT_METHODS.include?(input_method)
124
- execute_options[:environment] = envify_params(arguments)
125
- end
126
-
127
- stdout, stderr, exitcode = conn.execute(remote_task_path, execute_options)
128
- Bolt::Result.for_task(target,
129
- stdout,
130
- stderr,
131
- exitcode,
132
- task.name,
133
- position)
134
- end
135
- end
136
- end
137
-
138
- def connected?(target)
139
- with_connection(target) { true }
140
- rescue Bolt::Node::ConnectError
141
- false
142
- end
143
19
  end
144
20
  end
145
21
  end
@@ -5,227 +5,137 @@ require 'bolt/node/errors'
5
5
 
6
6
  module Bolt
7
7
  module Transport
8
- class Docker < Base
8
+ class Docker < Simple
9
9
  class Connection
10
+ attr_reader :user, :target
11
+
10
12
  def initialize(target)
11
13
  raise Bolt::ValidationError, "Target #{target.safe_name} does not have a host" unless target.host
12
14
  @target = target
15
+ @user = ENV['USER'] || Etc.getlogin
13
16
  @logger = Bolt::Logger.logger(target.safe_name)
14
- @docker_host = @target.options['service-url']
15
- @logger.trace("Initializing docker connection to #{@target.safe_name}")
17
+ @container_info = {}
18
+ @docker_host = target.options['service-url']
19
+ @logger.trace("Initializing docker connection to #{target.safe_name}")
20
+ end
21
+
22
+ def shell
23
+ @shell ||= if Bolt::Util.windows?
24
+ Bolt::Shell::Powershell.new(target, self)
25
+ else
26
+ Bolt::Shell::Bash.new(target, self)
27
+ end
28
+ end
29
+
30
+ # The full ID of the target container
31
+ #
32
+ # @return [String] The full ID of the target container
33
+ def container_id
34
+ @container_info["Id"]
35
+ end
36
+
37
+ private def env_hash
38
+ # Set the DOCKER_HOST if we are using a non-default service-url
39
+ @docker_host.nil? ? {} : { 'DOCKER_HOST' => @docker_host }
16
40
  end
17
41
 
18
42
  def connect
19
43
  # We don't actually have a connection, but we do need to
20
44
  # check that the container exists and is running.
21
- output = execute_local_docker_json_command('ps')
22
- index = output.find_index { |item| item["ID"] == @target.host || item["Names"] == @target.host }
23
- raise "Could not find a container with name or ID matching '#{@target.host}'" if index.nil?
45
+ output = execute_local_json_command('ps')
46
+ index = output.find_index { |item| item["ID"] == target.host || item["Names"] == target.host }
47
+ raise "Could not find a container with name or ID matching '#{target.host}'" if index.nil?
24
48
  # Now find the indepth container information
25
- output = execute_local_docker_json_command('inspect', [output[index]["ID"]])
49
+ output = execute_local_json_command('inspect', [output[index]["ID"]])
26
50
  # Store the container information for later
27
51
  @container_info = output[0]
28
52
  @logger.trace { "Opened session" }
29
53
  true
30
54
  rescue StandardError => e
31
55
  raise Bolt::Node::ConnectError.new(
32
- "Failed to connect to #{@target.safe_name}: #{e.message}",
56
+ "Failed to connect to #{target.safe_name}: #{e.message}",
33
57
  'CONNECT_ERROR'
34
58
  )
35
59
  end
36
60
 
37
- # Executes a command inside the target container
61
+ def add_env_vars(env_vars)
62
+ @env_vars = Bolt::Util.format_env_vars_for_cli(env_vars)
63
+ end
64
+
65
+ # Executes a command inside the target container. This is called from the shell class.
38
66
  #
39
- # @param command [Array] The command to run, expressed as an array of strings
40
- # @param options [Hash] command specific options
41
- # @option opts [String] :interpreter statements that are prefixed to the command e.g `/bin/bash` or `cmd.exe /c`
42
- # @option opts [Hash] :environment A hash of environment variables that will be injected into the command
43
- # @option opts [IO] :stdin An IO object that will be used to redirect STDIN for the docker command
44
- def execute(*command, options)
45
- command.unshift(options[:interpreter]) if options[:interpreter]
46
- # Build the `--env` parameters
47
- envs = []
48
- if options[:environment]
49
- options[:environment].each { |env, val| envs.concat(['--env', "#{env}=#{val}"]) }
67
+ # @param command [string] The command to run
68
+ def execute(command)
69
+ args = []
70
+ # CODEREVIEW: Is it always safe to pass --interactive?
71
+ args += %w[--interactive]
72
+ args += %w[--tty] if target.options['tty']
73
+ args += %W[--env DOCKER_HOST=#{@docker_host}] if @docker_host
74
+ args += @env_vars if @env_vars
75
+
76
+ if target.options['shell-command'] && !target.options['shell-command'].empty?
77
+ # escape any double quotes in command
78
+ command = command.gsub('"', '\"')
79
+ command = "#{target.options['shell-command']} \"#{command}\""
50
80
  end
51
81
 
52
- command_options = []
53
- # Need to be interactive if redirecting STDIN
54
- command_options << '--interactive' unless options[:stdin].nil?
55
- command_options << '--tty' if options[:tty]
56
- command_options.concat(envs) unless envs.empty?
57
- command_options << container_id
58
- command_options.concat(command)
59
-
60
- @logger.trace { "Executing: exec #{command_options}" }
82
+ docker_command = %w[docker exec] + args + [container_id] + Shellwords.split(command)
83
+ @logger.trace { "Executing: #{docker_command.join(' ')}" }
61
84
 
62
- stdout_str, stderr_str, status = execute_local_docker_command('exec', command_options, options[:stdin])
63
-
64
- # The actual result is the exitstatus not the process object
65
- status = status.nil? ? -32768 : status.exitstatus
66
- if status == 0
67
- @logger.trace { "Command returned successfully" }
68
- else
69
- @logger.trace { "Command failed with exit code #{status}" }
70
- end
71
- stdout_str.force_encoding(Encoding::UTF_8)
72
- stderr_str.force_encoding(Encoding::UTF_8)
73
- # Normalise line endings
74
- stdout_str.gsub!("\r\n", "\n")
75
- stderr_str.gsub!("\r\n", "\n")
76
- [stdout_str, stderr_str, status]
85
+ Open3.popen3(*docker_command)
77
86
  rescue StandardError
78
87
  @logger.trace { "Command aborted" }
79
88
  raise
80
89
  end
81
90
 
82
- def write_remote_file(source, destination)
83
- @logger.trace { "Uploading #{source} to #{destination}" }
84
- _, stdout_str, status = execute_local_docker_command('cp', [source, "#{container_id}:#{destination}"])
85
- unless status.exitstatus.zero?
86
- raise "Error writing file to container #{@container_id}: #{stdout_str}"
87
- end
88
- rescue StandardError => e
89
- raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
90
- end
91
-
92
- def write_remote_directory(source, destination)
91
+ def upload_file(source, destination)
93
92
  @logger.trace { "Uploading #{source} to #{destination}" }
94
- _, stdout_str, status = execute_local_docker_command('cp', [source, "#{container_id}:#{destination}"])
95
- unless status.exitstatus.zero?
96
- raise "Error writing directory to container #{@container_id}: #{stdout_str}"
93
+ _out, err, stat = Bolt::Util.exec_docker(['cp', source, "#{container_id}:#{destination}"], env_hash)
94
+ unless stat.exitstatus.zero?
95
+ raise "Error writing to container #{container_id}: #{err}"
97
96
  end
98
97
  rescue StandardError => e
99
98
  raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
100
99
  end
101
100
 
102
- def download_remote_content(source, destination)
101
+ def download_file(source, destination, _download)
103
102
  @logger.trace { "Downloading #{source} to #{destination}" }
104
103
  # Create the destination directory, otherwise copying a source directory with Docker will
105
104
  # copy the *contents* of the directory.
106
105
  # https://docs.docker.com/engine/reference/commandline/cp/
107
106
  FileUtils.mkdir_p(destination)
108
- _, stdout_str, status = execute_local_docker_command('cp', ["#{container_id}:#{source}", destination])
109
- unless status.exitstatus.zero?
110
- raise "Error downloading content from container #{@container_id}: #{stdout_str}"
107
+ _out, err, stat = Bolt::Util.exec_docker(['cp', "#{container_id}:#{source}", destination], env_hash)
108
+ unless stat.exitstatus.zero?
109
+ raise "Error downloading content from container #{container_id}: #{err}"
111
110
  end
112
111
  rescue StandardError => e
113
112
  raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
114
113
  end
115
114
 
116
- def mkdirs(dirs)
117
- _, stderr, exitcode = execute('mkdir', '-p', *dirs, {})
118
- if exitcode != 0
119
- message = "Could not create directories: #{stderr}"
120
- raise Bolt::Node::FileError.new(message, 'MKDIR_ERROR')
121
- end
122
- end
123
-
124
- def make_tmpdir
125
- tmpdir = @target.options.fetch('tmpdir', container_tmpdir)
126
- tmppath = "#{tmpdir}/#{SecureRandom.uuid}"
127
-
128
- stdout, stderr, exitcode = execute('mkdir', '-m', '700', tmppath, {})
129
- if exitcode != 0
130
- raise Bolt::Node::FileError.new("Could not make tmpdir: #{stderr}", 'TMPDIR_ERROR')
131
- end
132
- tmppath || stdout.first
133
- end
134
-
135
- def with_remote_tmpdir
136
- dir = make_tmpdir
137
- yield dir
138
- ensure
139
- if dir
140
- if @target.options['cleanup']
141
- _, stderr, exitcode = execute('rm', '-rf', dir, {})
142
- if exitcode != 0
143
- Bolt::Logger.warn("fail_cleanup", "Failed to clean up tmpdir '#{dir}': #{stderr}")
144
- end
145
- else
146
- Bolt::Logger.warn("skip_cleanup", "Skipping cleanup of tmpdir '#{dir}'")
147
- end
148
- end
149
- end
150
-
151
- def write_remote_executable(dir, file, filename = nil)
152
- filename ||= File.basename(file)
153
- remote_path = File.join(dir.to_s, filename)
154
- write_remote_file(file, remote_path)
155
- make_executable(remote_path)
156
- remote_path
157
- end
158
-
159
- def make_executable(path)
160
- _, stderr, exitcode = execute('chmod', 'u+x', path, {})
161
- if exitcode != 0
162
- message = "Could not make file '#{path}' executable: #{stderr}"
163
- raise Bolt::Node::FileError.new(message, 'CHMOD_ERROR')
164
- end
115
+ # Executes a Docker CLI command and parses the output in JSON format
116
+ #
117
+ # @param subcommand [String] The docker subcommand to run
118
+ # e.g. 'inspect' for `docker inspect`
119
+ # @param arguments [Array] Arguments to pass to the docker command
120
+ # e.g. 'src' and 'dest' for `docker cp <src> <dest>
121
+ # @return [Object] Ruby object representation of the JSON string
122
+ private def execute_local_json_command(subcommand, arguments = [])
123
+ cmd = [subcommand, '--format', '{{json .}}'].concat(arguments)
124
+ out, _err, _stat = Bolt::Util.exec_docker(cmd, env_hash)
125
+ extract_json(out)
165
126
  end
166
127
 
167
- private
168
-
169
128
  # Converts the JSON encoded STDOUT string from the docker cli into ruby objects
170
129
  #
171
- # @param stdout_string [String] The string to convert
130
+ # @param stdout [String] The string to convert
172
131
  # @return [Object] Ruby object representation of the JSON string
173
- def extract_json(stdout_string)
132
+ private def extract_json(stdout)
174
133
  # The output from the docker format command is a JSON string per line.
175
134
  # We can't do a direct convert but this helper method will convert it into
176
135
  # an array of Objects
177
- stdout_string.split("\n")
178
- .reject { |str| str.strip.empty? }
179
- .map { |str| JSON.parse(str) }
180
- end
181
-
182
- # rubocop:disable Layout/LineLength
183
- # Executes a Docker CLI command
184
- #
185
- # @param subcommand [String] The docker subcommand to run e.g. 'inspect' for `docker inspect`
186
- # @param command_options [Array] Additional command options e.g. ['--size'] for `docker inspect --size`
187
- # @param redir_stdin [IO] IO object which will be use to as STDIN in the docker command. Default is nil, which does not perform redirection
188
- # @return [String, String, Process::Status] The output of the command: STDOUT, STDERR, Process Status
189
- # rubocop:enable Layout/LineLength
190
- def execute_local_docker_command(subcommand, command_options = [], redir_stdin = nil)
191
- env_hash = {}
192
- # Set the DOCKER_HOST if we are using a non-default service-url
193
- env_hash['DOCKER_HOST'] = @docker_host unless @docker_host.nil?
194
-
195
- command_options = [] if command_options.nil?
196
- docker_command = [subcommand].concat(command_options)
197
-
198
- # Always use binary mode for any text data
199
- capture_options = { binmode: true }
200
- capture_options[:stdin_data] = redir_stdin unless redir_stdin.nil?
201
- stdout_str, stderr_str, status = Open3.capture3(env_hash, 'docker', *docker_command, capture_options)
202
- [stdout_str, stderr_str, status]
203
- end
204
-
205
- # Executes a Docker CLI command and parses the output in JSON format
206
- #
207
- # @param subcommand [String] The docker subcommand to run e.g. 'inspect' for `docker inspect`
208
- # @param command_options [Array] Additional command options e.g. ['--size'] for `docker inspect --size`
209
- # @return [Object] Ruby object representation of the JSON string
210
- def execute_local_docker_json_command(subcommand, command_options = [])
211
- command_options = [] if command_options.nil?
212
- command_options = ['--format', '{{json .}}'].concat(command_options)
213
- stdout_str, _stderr_str, _status = execute_local_docker_command(subcommand, command_options)
214
- extract_json(stdout_str)
215
- end
216
-
217
- # The full ID of the target container
218
- #
219
- # @return [String] The full ID of the target container
220
- def container_id
221
- @container_info["Id"]
222
- end
223
-
224
- # The temp path inside the target container
225
- #
226
- # @return [String] The absolute path to the temp directory
227
- def container_tmpdir
228
- '/tmp'
136
+ stdout.split("\n")
137
+ .reject { |str| str.strip.empty? }
138
+ .map { |str| JSON.parse(str) }
229
139
  end
230
140
  end
231
141
  end