bolt 2.5.0 → 2.6.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.

@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '2.5.0'
4
+ VERSION = '2.6.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bolt
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.0
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-13 00:00:00.000000000 Z
11
+ date: 2020-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: jwt
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.2'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.2'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: logging
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -471,6 +485,8 @@ files:
471
485
  - lib/bolt/shell.rb
472
486
  - lib/bolt/shell/bash.rb
473
487
  - lib/bolt/shell/bash/tmpdir.rb
488
+ - lib/bolt/shell/powershell.rb
489
+ - lib/bolt/shell/powershell/snippets.rb
474
490
  - lib/bolt/target.rb
475
491
  - lib/bolt/task.rb
476
492
  - lib/bolt/task/puppet_server.rb
@@ -480,10 +496,8 @@ files:
480
496
  - lib/bolt/transport/docker/connection.rb
481
497
  - lib/bolt/transport/local.rb
482
498
  - lib/bolt/transport/local/connection.rb
483
- - lib/bolt/transport/local_windows.rb
484
499
  - lib/bolt/transport/orch.rb
485
500
  - lib/bolt/transport/orch/connection.rb
486
- - lib/bolt/transport/powershell.rb
487
501
  - lib/bolt/transport/remote.rb
488
502
  - lib/bolt/transport/simple.rb
489
503
  - lib/bolt/transport/ssh.rb
@@ -1,189 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
- require 'fileutils'
5
- require 'open3'
6
- require 'tmpdir'
7
- require 'bolt/node/output'
8
- require 'bolt/transport/base'
9
- require 'bolt/transport/powershell'
10
- require 'bolt/util'
11
-
12
- module Bolt
13
- module Transport
14
- class LocalWindows < Base
15
- def provided_features
16
- ['powershell']
17
- end
18
-
19
- def default_input_method(executable)
20
- input_method ||= Powershell.powershell_file?(executable) ? 'powershell' : 'both'
21
- input_method
22
- end
23
-
24
- def in_tmpdir(base)
25
- args = base ? [nil, base] : []
26
- dir = begin
27
- Dir.mktmpdir(*args)
28
- rescue StandardError => e
29
- raise Bolt::Node::FileError.new("Could not make tempdir: #{e.message}", 'TEMPDIR_ERROR')
30
- end
31
-
32
- yield dir
33
- ensure
34
- FileUtils.remove_entry dir if dir
35
- end
36
- private :in_tmpdir
37
-
38
- def copy_file(source, destination)
39
- logger.debug { "Uploading #{source}, to #{destination}" }
40
- FileUtils.cp_r(source, destination, remove_destination: true)
41
- rescue StandardError => e
42
- raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
43
- end
44
-
45
- def with_tmpscript(script, base)
46
- in_tmpdir(base) do |dir|
47
- dest = File.join(dir, File.basename(script))
48
- copy_file(script, dest)
49
- File.chmod(0o750, dest)
50
- yield dest, dir
51
- end
52
- end
53
- private :with_tmpscript
54
-
55
- def execute(*command, options)
56
- # Interpreter can be an array or string. It will be appended to the command array.
57
- command.unshift(options[:interpreter]).flatten! if options[:interpreter]
58
- command = [options[:env]] + command if options[:env]
59
-
60
- if options[:stdin]
61
- stdout, stderr, rc = Open3.capture3(*command, stdin_data: options[:stdin], chdir: options[:dir])
62
- else
63
- stdout, stderr, rc = Open3.capture3(*command, chdir: options[:dir])
64
- end
65
-
66
- result_output = Bolt::Node::Output.new
67
- result_output.stdout << stdout unless stdout.nil?
68
- result_output.stderr << stderr unless stderr.nil?
69
- result_output.exit_code = rc.exitstatus
70
- result_output
71
- end
72
-
73
- def upload(target, source, destination, _options = {})
74
- copy_file(source, destination)
75
- Bolt::Result.for_upload(target, source, destination)
76
- end
77
-
78
- def run_command(target, command, _options = {})
79
- in_tmpdir(target.options['tmpdir']) do |dir|
80
- output = execute(command, dir: dir)
81
- Bolt::Result.for_command(target,
82
- output.stdout.string,
83
- output.stderr.string,
84
- output.exit_code,
85
- 'command', command)
86
- end
87
- end
88
-
89
- def run_script(target, script, arguments, _options = {})
90
- with_tmpscript(File.absolute_path(script), target.options['tmpdir']) do |file, dir|
91
- logger.debug "Running '#{file}' with #{arguments.to_json}"
92
-
93
- # unpack any Sensitive data AFTER we log
94
- arguments = unwrap_sensitive_args(arguments)
95
- if Powershell.powershell_file?(file)
96
- command = Powershell.run_script(arguments, file)
97
- interpreter = ['powershell.exe', *Powershell.ps_args]
98
- output = execute(command, dir: dir, interpreter: interpreter)
99
- else
100
- path, args = *Powershell.process_from_extension(file)
101
- args += Powershell.escape_arguments(arguments)
102
- command = args.unshift(path).join(' ')
103
- output = execute(command, dir: dir)
104
- end
105
- Bolt::Result.for_command(target,
106
- output.stdout.string,
107
- output.stderr.string,
108
- output.exit_code,
109
- 'script', script)
110
- end
111
- end
112
-
113
- def run_task(target, task, arguments, _options = {})
114
- implementation = select_implementation(target, task)
115
- executable = implementation['path']
116
- input_method = implementation['input_method']
117
- extra_files = implementation['files']
118
-
119
- in_tmpdir(target.options['tmpdir']) do |dir|
120
- if extra_files.empty?
121
- script = File.join(dir, File.basename(executable))
122
- else
123
- arguments['_installdir'] = dir
124
- script_dest = File.join(dir, task.tasks_dir)
125
- FileUtils.mkdir_p([script_dest] + extra_files.map { |file| File.join(dir, File.dirname(file['name'])) })
126
-
127
- script = File.join(script_dest, File.basename(executable))
128
- extra_files.each do |file|
129
- dest = File.join(dir, file['name'])
130
- copy_file(file['path'], dest)
131
- File.chmod(0o750, dest)
132
- end
133
- end
134
-
135
- copy_file(executable, script)
136
- File.chmod(0o750, script)
137
-
138
- interpreter = select_interpreter(script, target.options['interpreters'])
139
- interpreter_debug = interpreter ? " using '#{interpreter}' interpreter" : nil
140
- # log the arguments with sensitive data redacted, do NOT log unwrapped_arguments
141
- logger.debug("Running '#{script}' with #{arguments.to_json}#{interpreter_debug}")
142
- unwrapped_arguments = unwrap_sensitive_args(arguments)
143
-
144
- stdin = Bolt::Task::STDIN_METHODS.include?(input_method) ? JSON.dump(unwrapped_arguments) : nil
145
- if Bolt::Task::ENVIRONMENT_METHODS.include?(input_method)
146
- environment_params = envify_params(unwrapped_arguments).each_with_object([]) do |(arg, val), list|
147
- list << Powershell.set_env(arg, val)
148
- end
149
- environment_params = environment_params.join("\n") + "\n"
150
- else
151
- environment_params = ""
152
- end
153
-
154
- if Powershell.powershell_file?(script) && stdin.nil?
155
- command = Powershell.run_ps_task(arguments, script, input_method)
156
- command = environment_params + Powershell.shell_init + command
157
- interpreter ||= ['powershell.exe', *Powershell.ps_args]
158
- output =
159
- if input_method == 'powershell'
160
- execute(command, dir: dir, interpreter: interpreter)
161
- else
162
- execute(command, dir: dir, stdin: stdin, interpreter: interpreter)
163
- end
164
- end
165
- unless output
166
- if interpreter
167
- env = Bolt::Task::ENVIRONMENT_METHODS.include?(input_method) ? envify_params(unwrapped_arguments) : nil
168
- output = execute(script, stdin: stdin, env: env, dir: dir, interpreter: interpreter)
169
- else
170
- path, args = *Powershell.process_from_extension(script)
171
- command = args.unshift(path).join(' ')
172
- command = environment_params + Powershell.shell_init + command
173
- output = execute(command, dir: dir, stdin: stdin, interpreter: 'powershell.exe')
174
- end
175
- end
176
- Bolt::Result.for_task(target,
177
- output.stdout.string,
178
- output.stderr.string,
179
- output.exit_code,
180
- task.name)
181
- end
182
- end
183
-
184
- def connected?(_targets)
185
- true
186
- end
187
- end
188
- end
189
- end
@@ -1,337 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Bolt
4
- module Transport
5
- module Powershell
6
- class << self
7
- def ps_args
8
- %w[-NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass]
9
- end
10
-
11
- def powershell_file?(path)
12
- Pathname(path).extname.casecmp('.ps1').zero?
13
- end
14
-
15
- def process_from_extension(path)
16
- case Pathname(path).extname.downcase
17
- when '.rb'
18
- [
19
- 'ruby.exe',
20
- ['-S', "\"#{path}\""]
21
- ]
22
- when '.ps1'
23
- [
24
- 'powershell.exe',
25
- [*ps_args, '-File', "\"#{path}\""]
26
- ]
27
- when '.pp'
28
- [
29
- 'puppet.bat',
30
- ['apply', "\"#{path}\""]
31
- ]
32
- else
33
- # Run the script via cmd, letting Windows extension handling determine how
34
- [
35
- 'cmd.exe',
36
- ['/c', "\"#{path}\""]
37
- ]
38
- end
39
- end
40
-
41
- def escape_arguments(arguments)
42
- arguments.map do |arg|
43
- if arg =~ / /
44
- "\"#{arg}\""
45
- else
46
- arg
47
- end
48
- end
49
- end
50
-
51
- def set_env(arg, val)
52
- "[Environment]::SetEnvironmentVariable('#{arg}', @'\n#{val}\n'@)"
53
- end
54
-
55
- def quote_string(string)
56
- "'" + string.gsub("'", "''") + "'"
57
- end
58
-
59
- def execute_process(path, arguments, stdin = nil)
60
- quoted_args = arguments.map { |arg| quote_string(arg) }.join(' ')
61
-
62
- quoted_path = if path =~ /^'.*'$/ || path =~ /^".*"$/
63
- path
64
- else
65
- quote_string(path)
66
- end
67
- exec_cmd =
68
- if stdin.nil?
69
- "& #{quoted_path} #{quoted_args}"
70
- else
71
- "@'\n#{stdin}\n'@ | & #{quoted_path} #{quoted_args}"
72
- end
73
- <<-PS
74
- $OutputEncoding = [Console]::OutputEncoding
75
- #{exec_cmd}
76
- if (-not $? -and ($LASTEXITCODE -eq $null)) { exit 1 }
77
- exit $LASTEXITCODE
78
- PS
79
- end
80
-
81
- def mkdirs(dirs)
82
- "mkdir -Force #{dirs.uniq.sort.join(',')}"
83
- end
84
-
85
- def make_tempdir(parent)
86
- <<-PS
87
- $parent = #{parent}
88
- $name = [System.IO.Path]::GetRandomFileName()
89
- $path = Join-Path $parent $name
90
- New-Item -ItemType Directory -Path $path | Out-Null
91
- $path
92
- PS
93
- end
94
-
95
- def rmdir(dir)
96
- <<-PS
97
- Remove-Item -Force -Recurse -Path "#{dir}"
98
- PS
99
- end
100
-
101
- def run_script(arguments, script_path)
102
- mapped_args = arguments.map do |a|
103
- "$invokeArgs.ArgumentList += @'\n#{a}\n'@"
104
- end.join("\n")
105
- <<-PS
106
- $invokeArgs = @{
107
- ScriptBlock = (Get-Command "#{script_path}").ScriptBlock
108
- ArgumentList = @()
109
- }
110
- #{mapped_args}
111
-
112
- try
113
- {
114
- Invoke-Command @invokeArgs
115
- }
116
- catch
117
- {
118
- Write-Error $_.Exception
119
- exit 1
120
- }
121
- PS
122
- end
123
-
124
- def run_ps_task(arguments, task_path, input_method)
125
- # NOTE: cannot redirect STDIN to a .ps1 script inside of PowerShell
126
- # must create new powershell.exe process like other interpreters
127
- # fortunately, using PS with stdin input_method should never happen
128
- if input_method == 'powershell'
129
- <<-PS
130
- $private:tempArgs = Get-ContentAsJson (
131
- $utf8.GetString([System.Convert]::FromBase64String('#{Base64.encode64(JSON.dump(arguments))}'))
132
- )
133
- $allowedArgs = (Get-Command "#{task_path}").Parameters.Keys
134
- $private:taskArgs = @{}
135
- $private:tempArgs.Keys | ? { $allowedArgs -contains $_ } | % { $private:taskArgs[$_] = $private:tempArgs[$_] }
136
- try { & "#{task_path}" @taskArgs } catch { Write-Error $_.Exception; exit 1 }
137
- PS
138
- else
139
- %(try { & "#{task_path}" } catch { Write-Error $_.Exception; exit 1 })
140
- end
141
- end
142
-
143
- def shell_init
144
- <<-PS
145
- $ENV:PATH += ";${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\bin\\;" +
146
- "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\puppet\\bin;" +
147
- "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\sys\\ruby\\bin\\"
148
- $ENV:RUBYLIB = "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\puppet\\lib;" +
149
- "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\facter\\lib;" +
150
- "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\hiera\\lib;" +
151
- $ENV:RUBYLIB
152
-
153
- Add-Type -AssemblyName System.ServiceModel.Web, System.Runtime.Serialization
154
- $utf8 = [System.Text.Encoding]::UTF8
155
-
156
- function Write-Stream {
157
- PARAM(
158
- [Parameter(Position=0)] $stream,
159
- [Parameter(ValueFromPipeline=$true)] $string
160
- )
161
- PROCESS {
162
- $bytes = $utf8.GetBytes($string)
163
- $stream.Write( $bytes, 0, $bytes.Length )
164
- }
165
- }
166
-
167
- function Convert-JsonToXml {
168
- PARAM([Parameter(ValueFromPipeline=$true)] [string[]] $json)
169
- BEGIN {
170
- $mStream = New-Object System.IO.MemoryStream
171
- }
172
- PROCESS {
173
- $json | Write-Stream -Stream $mStream
174
- }
175
- END {
176
- $mStream.Position = 0
177
- try {
178
- $jsonReader = [System.Runtime.Serialization.Json.JsonReaderWriterFactory]::CreateJsonReader($mStream,[System.Xml.XmlDictionaryReaderQuotas]::Max)
179
- $xml = New-Object Xml.XmlDocument
180
- $xml.Load($jsonReader)
181
- $xml
182
- } finally {
183
- $jsonReader.Close()
184
- $mStream.Dispose()
185
- }
186
- }
187
- }
188
-
189
- Function ConvertFrom-Xml {
190
- [CmdletBinding(DefaultParameterSetName="AutoType")]
191
- PARAM(
192
- [Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [Xml.XmlNode] $xml,
193
- [Parameter(Mandatory=$true,ParameterSetName="ManualType")] [Type] $Type,
194
- [Switch] $ForceType
195
- )
196
- PROCESS{
197
- if (Get-Member -InputObject $xml -Name root) {
198
- return $xml.root.Objects | ConvertFrom-Xml
199
- } elseif (Get-Member -InputObject $xml -Name Objects) {
200
- return $xml.Objects | ConvertFrom-Xml
201
- }
202
- $propbag = @{}
203
- foreach ($name in Get-Member -InputObject $xml -MemberType Properties | Where-Object{$_.Name -notmatch "^(__.*|type)$"} | Select-Object -ExpandProperty name) {
204
- Write-Debug "$Name Type: $($xml.$Name.type)" -Debug:$false
205
- $propbag."$Name" = Convert-Properties $xml."$name"
206
- }
207
- if (!$Type -and $xml.HasAttribute("__type")) { $Type = $xml.__Type }
208
- if ($ForceType -and $Type) {
209
- try {
210
- $output = New-Object $Type -Property $propbag
211
- } catch {
212
- $output = New-Object PSObject -Property $propbag
213
- $output.PsTypeNames.Insert(0, $xml.__type)
214
- }
215
- } elseif ($propbag.Count -ne 0) {
216
- $output = New-Object PSObject -Property $propbag
217
- if ($Type) {
218
- $output.PsTypeNames.Insert(0, $Type)
219
- }
220
- }
221
- return $output
222
- }
223
- }
224
-
225
- Function Convert-Properties {
226
- PARAM($InputObject)
227
- switch ($InputObject.type) {
228
- "object" {
229
- return (ConvertFrom-Xml -Xml $InputObject)
230
- }
231
- "string" {
232
- $MightBeADate = $InputObject.get_InnerText() -as [DateTime]
233
- ## Strings that are actually dates (*grumble* JSON is crap)
234
- if ($MightBeADate -and $propbag."$Name" -eq $MightBeADate.ToString("G")) {
235
- return $MightBeADate
236
- } else {
237
- return $InputObject.get_InnerText()
238
- }
239
- }
240
- "number" {
241
- $number = $InputObject.get_InnerText()
242
- if ($number -eq ($number -as [int])) {
243
- return $number -as [int]
244
- } elseif ($number -eq ($number -as [double])) {
245
- return $number -as [double]
246
- } else {
247
- return $number -as [decimal]
248
- }
249
- }
250
- "boolean" {
251
- return [bool]::parse($InputObject.get_InnerText())
252
- }
253
- "null" {
254
- return $null
255
- }
256
- "array" {
257
- [object[]]$Items = $(foreach( $item in $InputObject.GetEnumerator() ) {
258
- Convert-Properties $item
259
- })
260
- return $Items
261
- }
262
- default {
263
- return $InputObject
264
- }
265
- }
266
- }
267
-
268
- Function ConvertFrom-Json2 {
269
- [CmdletBinding()]
270
- PARAM(
271
- [Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [string] $InputObject,
272
- [Parameter(Mandatory=$true)] [Type] $Type,
273
- [Switch] $ForceType
274
- )
275
- PROCESS {
276
- $null = $PSBoundParameters.Remove("InputObject")
277
- [Xml.XmlElement]$xml = (Convert-JsonToXml $InputObject).Root
278
- if ($xml) {
279
- if ($xml.Objects) {
280
- $xml.Objects.Item.GetEnumerator() | ConvertFrom-Xml @PSBoundParameters
281
- } elseif ($xml.Item -and $xml.Item -isnot [System.Management.Automation.PSParameterizedProperty]) {
282
- $xml.Item | ConvertFrom-Xml @PSBoundParameters
283
- } else {
284
- $xml | ConvertFrom-Xml @PSBoundParameters
285
- }
286
- } else {
287
- Write-Error "Failed to parse JSON with JsonReader" -Debug:$false
288
- }
289
- }
290
- }
291
-
292
- function ConvertFrom-PSCustomObject
293
- {
294
- PARAM([Parameter(ValueFromPipeline = $true)] $InputObject)
295
- PROCESS {
296
- if ($null -eq $InputObject) { return $null }
297
-
298
- if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]) {
299
- $collection = @(
300
- foreach ($object in $InputObject) { ConvertFrom-PSCustomObject $object }
301
- )
302
-
303
- $collection
304
- } elseif ($InputObject -is [System.Management.Automation.PSCustomObject]) {
305
- $hash = @{}
306
- foreach ($property in $InputObject.PSObject.Properties) {
307
- $hash[$property.Name] = ConvertFrom-PSCustomObject $property.Value
308
- }
309
-
310
- $hash
311
- } else {
312
- $InputObject
313
- }
314
- }
315
- }
316
-
317
- function Get-ContentAsJson
318
- {
319
- [CmdletBinding()]
320
- PARAM(
321
- [Parameter(Mandatory = $true)] $Text,
322
- [Parameter(Mandatory = $false)] [Text.Encoding] $Encoding = [Text.Encoding]::UTF8
323
- )
324
-
325
- # using polyfill cmdlet on PS2, so pass type info
326
- if ($PSVersionTable.PSVersion -lt [Version]'3.0') {
327
- $Text | ConvertFrom-Json2 -Type PSObject | ConvertFrom-PSCustomObject
328
- } else {
329
- $Text | ConvertFrom-Json | ConvertFrom-PSCustomObject
330
- }
331
- }
332
- PS
333
- end
334
- end
335
- end
336
- end
337
- end