bolt 0.7.0 → 0.8.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/lib/bolt/cli.rb +65 -28
  3. data/lib/bolt/config.rb +18 -8
  4. data/lib/bolt/executor.rb +21 -6
  5. data/lib/bolt/node.rb +3 -0
  6. data/lib/bolt/node/result.rb +5 -0
  7. data/lib/bolt/node/ssh.rb +81 -25
  8. data/lib/bolt/node/winrm.rb +70 -31
  9. data/lib/bolt/notifier.rb +20 -0
  10. data/lib/bolt/outputter.rb +21 -0
  11. data/lib/bolt/outputter/human.rb +30 -0
  12. data/lib/bolt/outputter/json.rb +51 -0
  13. data/lib/bolt/result.rb +32 -5
  14. data/lib/bolt/version.rb +1 -1
  15. data/vendored/puppet/lib/puppet.rb +4 -5
  16. data/vendored/puppet/lib/puppet/agent.rb +22 -2
  17. data/vendored/puppet/lib/puppet/application/agent.rb +1 -1
  18. data/vendored/puppet/lib/puppet/application/apply.rb +1 -1
  19. data/vendored/puppet/lib/puppet/configurer/downloader_factory.rb +10 -0
  20. data/vendored/puppet/lib/puppet/configurer/plugin_handler.rb +4 -4
  21. data/vendored/puppet/lib/puppet/defaults.rb +21 -2
  22. data/vendored/puppet/lib/puppet/external/nagios/parser.rb +1 -1
  23. data/vendored/puppet/lib/puppet/file_serving/configuration.rb +3 -0
  24. data/vendored/puppet/lib/puppet/file_serving/configuration/parser.rb +2 -0
  25. data/vendored/puppet/lib/puppet/file_serving/mount/locales.rb +35 -0
  26. data/vendored/puppet/lib/puppet/forge.rb +9 -3
  27. data/vendored/puppet/lib/puppet/forge/repository.rb +1 -1
  28. data/vendored/puppet/lib/puppet/functions/file_upload.rb +20 -15
  29. data/vendored/puppet/lib/puppet/functions/new.rb +1 -4
  30. data/vendored/puppet/lib/puppet/functions/run_command.rb +15 -13
  31. data/vendored/puppet/lib/puppet/functions/run_script.rb +27 -14
  32. data/vendored/puppet/lib/puppet/functions/run_task.rb +21 -19
  33. data/vendored/puppet/lib/puppet/gettext/config.rb +86 -28
  34. data/vendored/puppet/lib/puppet/indirector/catalog/compiler.rb +25 -5
  35. data/vendored/puppet/lib/puppet/indirector/file_bucket_file/file.rb +1 -1
  36. data/vendored/puppet/lib/puppet/module.rb +13 -17
  37. data/vendored/puppet/lib/puppet/pops/evaluator/access_operator.rb +20 -21
  38. data/vendored/puppet/lib/puppet/pops/evaluator/compare_operator.rb +3 -3
  39. data/vendored/puppet/lib/puppet/pops/evaluator/evaluator_impl.rb +9 -0
  40. data/vendored/puppet/lib/puppet/pops/loader/static_loader.rb +20 -1
  41. data/vendored/puppet/lib/puppet/pops/loader/task_instantiator.rb +2 -1
  42. data/vendored/puppet/lib/puppet/pops/loaders.rb +6 -41
  43. data/vendored/puppet/lib/puppet/pops/pcore.rb +9 -0
  44. data/vendored/puppet/lib/puppet/pops/serialization/from_data_converter.rb +64 -10
  45. data/vendored/puppet/lib/puppet/pops/serialization/json_path.rb +2 -1
  46. data/vendored/puppet/lib/puppet/pops/types/execution_result.rb +7 -4
  47. data/vendored/puppet/lib/puppet/pops/types/p_binary_type.rb +9 -2
  48. data/vendored/puppet/lib/puppet/pops/types/p_init_type.rb +1 -1
  49. data/vendored/puppet/lib/puppet/pops/types/p_meta_type.rb +4 -0
  50. data/vendored/puppet/lib/puppet/pops/types/p_object_type.rb +81 -4
  51. data/vendored/puppet/lib/puppet/pops/types/p_object_type_extension.rb +213 -0
  52. data/vendored/puppet/lib/puppet/pops/types/p_sem_ver_type.rb +10 -2
  53. data/vendored/puppet/lib/puppet/pops/types/puppet_object.rb +11 -1
  54. data/vendored/puppet/lib/puppet/pops/types/type_calculator.rb +2 -2
  55. data/vendored/puppet/lib/puppet/pops/types/type_factory.rb +16 -6
  56. data/vendored/puppet/lib/puppet/pops/types/type_formatter.rb +22 -14
  57. data/vendored/puppet/lib/puppet/pops/types/type_parser.rb +17 -15
  58. data/vendored/puppet/lib/puppet/pops/types/types.rb +181 -72
  59. data/vendored/puppet/lib/puppet/provider.rb +18 -8
  60. data/vendored/puppet/lib/puppet/provider/package/yum.rb +22 -7
  61. data/vendored/puppet/lib/puppet/provider/service/base.rb +21 -8
  62. data/vendored/puppet/lib/puppet/provider/service/launchd.rb +2 -3
  63. data/vendored/puppet/lib/puppet/provider/user/aix.rb +1 -0
  64. data/vendored/puppet/lib/puppet/provider/user/user_role_add.rb +7 -1
  65. data/vendored/puppet/lib/puppet/provider/user/useradd.rb +3 -2
  66. data/vendored/puppet/lib/puppet/provider/zfs/zfs.rb +5 -1
  67. data/vendored/puppet/lib/puppet/type/exec.rb +5 -4
  68. data/vendored/puppet/lib/puppet/type/macauthorization.rb +1 -1
  69. data/vendored/puppet/lib/puppet/type/user.rb +19 -0
  70. data/vendored/puppet/lib/puppet/util/log/destinations.rb +10 -0
  71. data/vendored/puppet/lib/puppet/util/windows/file.rb +35 -4
  72. data/vendored/puppet/lib/puppet/vendor/semantic_puppet/lib/semantic_puppet.rb +1 -1
  73. data/vendored/puppet/lib/puppet/version.rb +1 -1
  74. data/vendored/puppet/lib/puppet_pal.rb +15 -5
  75. metadata +8 -3
  76. data/vendored/puppet/lib/puppet/pops/types/p_error_type.rb +0 -158
@@ -69,38 +69,76 @@ function Invoke-Interpreter
69
69
  $StdinInput = $Null
70
70
  )
71
71
 
72
- $startInfo = New-Object System.Diagnostics.ProcessStartInfo($Path, $Arguments)
73
- $startInfo.UseShellExecute = $false
74
- $startInfo.WorkingDirectory = Split-Path -Parent (Get-Command $Path).Path
75
- if ($StdinInput) { $startInfo.RedirectStandardInput = $true }
76
- $startInfo.RedirectStandardOutput = $true
77
- $startInfo.RedirectStandardError = $true
78
-
79
72
  try
80
73
  {
81
- $process = [System.Diagnostics.Process]::Start($startInfo)
74
+ if (-not (Get-Command $Path -ErrorAction SilentlyContinue))
75
+ {
76
+ throw "Could not find executable '$Path' in ${ENV:PATH} on target node"
77
+ }
78
+
79
+ $startInfo = New-Object System.Diagnostics.ProcessStartInfo($Path, $Arguments)
80
+ $startInfo.UseShellExecute = $false
81
+ $startInfo.WorkingDirectory = Split-Path -Parent (Get-Command $Path).Path
82
+ $startInfo.CreateNoWindow = $true
83
+ if ($StdinInput) { $startInfo.RedirectStandardInput = $true }
84
+ $startInfo.RedirectStandardOutput = $true
85
+ $startInfo.RedirectStandardError = $true
86
+
87
+ $stdoutHandler = { if (-not ([String]::IsNullOrEmpty($EventArgs.Data))) { $Host.UI.WriteLine($EventArgs.Data) } }
88
+ $stderrHandler = { if (-not ([String]::IsNullOrEmpty($EventArgs.Data))) { $Host.UI.WriteErrorLine($EventArgs.Data) } }
89
+ $invocationId = [Guid]::NewGuid().ToString()
90
+
91
+ $process = New-Object System.Diagnostics.Process
92
+ $process.StartInfo = $startInfo
93
+ $process.EnableRaisingEvents = $true
94
+
95
+ # https://msdn.microsoft.com/en-us/library/system.diagnostics.process.standarderror(v=vs.110).aspx#Anchor_2
96
+ $stdoutEvent = Register-ObjectEvent -InputObject $process -EventName 'OutputDataReceived' -Action $stdoutHandler
97
+ $stderrEvent = Register-ObjectEvent -InputObject $process -EventName 'ErrorDataReceived' -Action $stderrHandler
98
+ $exitedEvent = Register-ObjectEvent -InputObject $process -EventName 'Exited' -SourceIdentifier $invocationId
99
+
100
+ $process.Start() | Out-Null
101
+
102
+ $process.BeginOutputReadLine()
103
+ $process.BeginErrorReadLine()
104
+
105
+ if ($StdinInput)
106
+ {
107
+ $process.StandardInput.WriteLine($StdinInput)
108
+ $process.StandardInput.Close()
109
+ }
110
+
111
+ # park current thread until the PS event is signaled upon process exit
112
+ # OR the timeout has elapsed
113
+ $waitResult = Wait-Event -SourceIdentifier $invocationId -Timeout $Timeout
114
+ if (! $process.HasExited)
115
+ {
116
+ $Host.UI.WriteErrorLine("Process $Path did not complete in $Timeout seconds")
117
+ return 1
118
+ }
119
+
120
+ return $process.ExitCode
82
121
  }
83
122
  catch
84
123
  {
85
- Write-Error $_
124
+ $Host.UI.WriteErrorLine($_)
86
125
  return 1
87
126
  }
88
-
89
- if ($StdinInput)
127
+ finally
90
128
  {
91
- $process.StandardInput.WriteLine($StdinInput)
92
- $process.StandardInput.Close()
129
+ @($stdoutEvent, $stderrEvent, $exitedEvent) |
130
+ ? { $_ -ne $Null } |
131
+ % { Unregister-Event -SourceIdentifier $_.Name }
132
+
133
+ if ($process -ne $Null)
134
+ {
135
+ if (($process.Handle -ne $Null) -and (! $process.HasExited))
136
+ {
137
+ try { $process.Kill() } catch { $Host.UI.WriteErrorLine("Failed To Kill Process $Path") }
138
+ }
139
+ $process.Dispose()
140
+ }
93
141
  }
94
-
95
- # streams must have .ReadToEnd() called prior to process .WaitForExit()
96
- # to prevent deadlocks per MSDN
97
- # https://msdn.microsoft.com/en-us/library/system.diagnostics.process.standarderror(v=vs.110).aspx#Anchor_2
98
- $process.StandardOutput.ReadToEnd() | Out-Host
99
- $stderr = $process.StandardError.ReadToEnd()
100
- if ($stderr) { Write-Error $stderr }
101
- $process.WaitForExit($Timeout) | Out-Null
102
-
103
- return $process.ExitCode
104
142
  }
105
143
  PS
106
144
  @shell_initialized = true
@@ -128,11 +166,11 @@ PS
128
166
  end
129
167
  end
130
168
 
131
- # 10 minutes in milliseconds
132
- DEFAULT_EXECUTION_TIMEOUT = 10 * 60 * 1000
169
+ # 10 minutes in seconds
170
+ DEFAULT_EXECUTION_TIMEOUT = 10 * 60
133
171
 
134
172
  def execute_process(path = '', arguments = [], stdin = nil,
135
- timeout_ms = DEFAULT_EXECUTION_TIMEOUT)
173
+ timeout = DEFAULT_EXECUTION_TIMEOUT)
136
174
  quoted_args = arguments.map do |arg|
137
175
  "'" + arg.gsub("'", "''") + "'"
138
176
  end.join(',')
@@ -145,12 +183,13 @@ $quoted_array = @(
145
183
  $invokeArgs = @{
146
184
  Path = "#{path}"
147
185
  Arguments = $quoted_array -Join ' '
148
- Timeout = #{timeout_ms}
186
+ Timeout = #{timeout}
149
187
  #{stdin.nil? ? '' : "StdinInput = @'\n" + stdin + "\n'@"}
150
188
  }
151
189
 
152
- # winrm gem relies on $LASTEXITCODE
153
- $LASTEXITCODE = Invoke-Interpreter @invokeArgs
190
+ # winrm gem checks $? prior to using $LASTEXITCODE
191
+ # making it necessary to exit with the desired code to propagate status properly
192
+ exit $(Invoke-Interpreter @invokeArgs)
154
193
  PS
155
194
  end
156
195
 
@@ -233,9 +272,9 @@ PS
233
272
  def _run_script(script, arguments)
234
273
  @logger.info { "Running script '#{script}'" }
235
274
  with_remote_file(script) do |remote_path|
236
- args = [*PS_ARGS, '-File', "\"#{remote_path}\""]
275
+ path, args = *process_from_extension(remote_path)
237
276
  args += escape_arguments(arguments)
238
- execute_process('powershell.exe', args)
277
+ execute_process(path, args)
239
278
  end
240
279
  end
241
280
 
@@ -0,0 +1,20 @@
1
+ require 'concurrent'
2
+
3
+ module Bolt
4
+ class Notifier
5
+ def initialize(executor = Concurrent::SingleThreadExecutor.new)
6
+ @executor = executor
7
+ end
8
+
9
+ def notify(callback, node, result)
10
+ @executor.post do
11
+ callback.call(node, result)
12
+ end
13
+ end
14
+
15
+ def shutdown
16
+ @executor.shutdown
17
+ @executor.wait_for_termination
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ module Bolt
2
+ class Outputter
3
+ def self.for_format(format)
4
+ case format
5
+ when 'human'
6
+ Bolt::Outputter::Human.new
7
+ when 'json'
8
+ Bolt::Outputter::JSON.new
9
+ when nil
10
+ raise "Cannot use outputter before parsing."
11
+ end
12
+ end
13
+
14
+ def initialize(stream = $stdout)
15
+ @stream = stream
16
+ end
17
+ end
18
+ end
19
+
20
+ require 'bolt/outputter/human'
21
+ require 'bolt/outputter/json'
@@ -0,0 +1,30 @@
1
+ module Bolt
2
+ class Outputter
3
+ class Human < Bolt::Outputter
4
+ def print_head; end
5
+
6
+ def print_result(node, result)
7
+ color = result.success? ? "\033[32m" : "\033[31m"
8
+ @stream.print color if @stream.isatty
9
+ @stream.puts "#{node.host}:"
10
+ @stream.print "\033[0m" if @stream.isatty
11
+ @stream.puts
12
+ @stream.puts result.message
13
+ @stream.puts
14
+ end
15
+
16
+ def print_summary(results, elapsed_time)
17
+ @stream.puts format("Ran on %d node%s in %.2f seconds",
18
+ results.size,
19
+ results.size > 1 ? 's' : '',
20
+ elapsed_time)
21
+ end
22
+
23
+ def print_plan(result)
24
+ @stream.puts result
25
+ end
26
+
27
+ def fatal_error(e); end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,51 @@
1
+ module Bolt
2
+ class Outputter
3
+ class JSON < Bolt::Outputter
4
+ def initialize(stream = $stdout)
5
+ @items_open = false
6
+ @object_open = false
7
+ @preceding_item = false
8
+ super(stream)
9
+ end
10
+
11
+ def print_head
12
+ @stream.puts '{ "items": ['
13
+ @preceding_item = false
14
+ @items_open = true
15
+ @object_open = true
16
+ end
17
+
18
+ def print_result(node, result)
19
+ item = {
20
+ name: node.uri,
21
+ status: result.is_a?(Bolt::ErrorResult) ? 'failure' : 'success',
22
+ result: result.to_result
23
+ }
24
+
25
+ @stream.puts ',' if @preceding_item
26
+ @stream.puts item.to_json
27
+ @preceding_item = true
28
+ end
29
+
30
+ def print_summary(results, elapsed_time)
31
+ @stream.puts "],\n"
32
+ @preceding_item = false
33
+ @items_open = false
34
+ @stream.puts format('"node_count": %d, "elapsed_time": %d }',
35
+ results.size,
36
+ elapsed_time)
37
+ end
38
+
39
+ def print_plan(result)
40
+ @stream.puts result.to_json
41
+ end
42
+
43
+ def fatal_error(e)
44
+ @stream.puts "],\n" if @items_open
45
+ @stream.puts '"_error": ' if @object_open
46
+ @stream.puts e.to_json
47
+ @stream.puts '}' if @object_open
48
+ end
49
+ end
50
+ end
51
+ end
@@ -20,6 +20,10 @@ module Bolt
20
20
  { 'value' => value }
21
21
  end
22
22
 
23
+ def to_result
24
+ value
25
+ end
26
+
23
27
  def success?
24
28
  true
25
29
  end
@@ -42,6 +46,16 @@ module Bolt
42
46
  }
43
47
  end
44
48
 
49
+ def to_result
50
+ {
51
+ '_error' => {
52
+ 'issue_code' => @issue_code,
53
+ 'kind' => @kind,
54
+ 'msg' => @message
55
+ }
56
+ }
57
+ end
58
+
45
59
  def success?
46
60
  false
47
61
  end
@@ -64,6 +78,10 @@ module Bolt
64
78
  }
65
79
  end
66
80
 
81
+ def to_result
82
+ value
83
+ end
84
+
67
85
  def success?
68
86
  @exit_code.zero?
69
87
  end
@@ -86,6 +104,12 @@ module Bolt
86
104
  @object || @stdout
87
105
  end
88
106
 
107
+ def to_result
108
+ result = @object
109
+ result['_error'] = @error if @error
110
+ result
111
+ end
112
+
89
113
  def to_h
90
114
  hash = super
91
115
  hash['error'] = error if error
@@ -95,12 +119,15 @@ module Bolt
95
119
  private
96
120
 
97
121
  def output_to_json_hash(output)
98
- obj = JSON.parse(output)
99
- if obj.is_a? Hash
100
- obj
122
+ begin
123
+ obj = JSON.parse(output)
124
+ unless obj.is_a? Hash
125
+ obj = nil
126
+ end
127
+ rescue JSON::ParserError
128
+ nil
101
129
  end
102
- rescue JSON::ParserError
103
- nil
130
+ obj || { '_output' => output }
104
131
  end
105
132
  end
106
133
 
@@ -1,3 +1,3 @@
1
1
  module Bolt
2
- VERSION = '0.7.0'.freeze
2
+ VERSION = '0.8.0'.freeze
3
3
  end
@@ -42,11 +42,10 @@ module Puppet
42
42
  require 'puppet/environments'
43
43
 
44
44
  class << self
45
- gettext_config_file = Puppet::GettextConfig.puppet_locale_path
46
- unless Puppet::GettextConfig.initialize(gettext_config_file, Puppet::GettextConfig.translation_mode(gettext_config_file))
47
- # Stub out gettext's `_` and `n_()` methods, which attempt to load translations,
48
- # with versions that do nothing
49
- require 'puppet/gettext/stubs'
45
+ Puppet::GettextConfig.create_text_domain('production')
46
+ locale_dir = Puppet::GettextConfig.puppet_locale_path
47
+ if Puppet::GettextConfig.load_translations('puppet', locale_dir, Puppet::GettextConfig.translation_mode(locale_dir))
48
+ Puppet::GettextConfig.set_locale(Locale.current.language)
50
49
  end
51
50
 
52
51
  include Puppet::Util
@@ -2,6 +2,8 @@ require 'puppet/application'
2
2
  require 'puppet/error'
3
3
  require 'puppet/util/at_fork'
4
4
 
5
+ require 'timeout'
6
+
5
7
  # A general class for triggering a run of another
6
8
  # class.
7
9
  class Puppet::Agent
@@ -14,6 +16,10 @@ class Puppet::Agent
14
16
  require 'puppet/util/splayer'
15
17
  include Puppet::Util::Splayer
16
18
 
19
+ # Special exception class used to signal an agent run has timed out.
20
+ class RunTimeoutError < Exception
21
+ end
22
+
17
23
  attr_reader :client_class, :client, :should_fork
18
24
 
19
25
  def initialize(client_class, should_fork=true)
@@ -41,12 +47,26 @@ class Puppet::Agent
41
47
  splay client_options.fetch :splay, Puppet[:splay]
42
48
  result = run_in_fork(should_fork) do
43
49
  with_client(client_options[:transaction_uuid], client_options[:job_id]) do |client|
50
+ client_args = client_options.merge(:pluginsync => Puppet::Configurer.should_pluginsync?)
44
51
  begin
45
- client_args = client_options.merge(:pluginsync => Puppet::Configurer.should_pluginsync?)
46
- lock { client.run(client_args) }
52
+ lock do
53
+ # NOTE: Timeout is pretty heinous as the location in which it
54
+ # throws an error is entirely unpredictable, which means that
55
+ # it can interrupt code blocks that perform cleanup or enforce
56
+ # sanity. The only thing a Puppet agent should do after this
57
+ # error is thrown is die with as much dignity as possible.
58
+ Timeout.timeout(Puppet[:runtimeout], RunTimeoutError) do
59
+ client.run(client_args)
60
+ end
61
+ end
47
62
  rescue Puppet::LockError
48
63
  Puppet.notice _("Run of %{client_class} already in progress; skipping (%{lockfile_path} exists)") % { client_class: client_class, lockfile_path: lockfile_path }
49
64
  return
65
+ rescue RunTimeoutError => detail
66
+ Puppet.log_exception(detail, _("Execution of %{client_class} did not complete within %{runtimeout} seconds and was terminated.") %
67
+ {client_class: client_class,
68
+ runtimeout: Puppet[:runtimeout]})
69
+ return 1
50
70
  rescue StandardError => detail
51
71
  Puppet.log_exception(detail, _("Could not run %{client_class}: %{detail}") % { client_class: client_class, detail: detail })
52
72
  1
@@ -98,7 +98,7 @@ USAGE
98
98
  -----
99
99
  puppet agent [--certname <NAME>] [-D|--daemonize|--no-daemonize]
100
100
  [-d|--debug] [--detailed-exitcodes] [--digest <DIGEST>] [--disable [MESSAGE]] [--enable]
101
- [--fingerprint] [-h|--help] [-l|--logdest syslog|eventlog|<FILE>|console]
101
+ [--fingerprint] [-h|--help] [-l|--logdest syslog|eventlog|<ABS FILEPATH>|console]
102
102
  [--masterport <PORT>] [--noop] [-o|--onetime] [-t|--test]
103
103
  [-v|--verbose] [-V|--version] [-w|--waitforcert <SECONDS>]
104
104
 
@@ -50,7 +50,7 @@ USAGE
50
50
  -----
51
51
  puppet apply [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose]
52
52
  [-e|--execute] [--detailed-exitcodes] [-L|--loadclasses]
53
- [-l|--logdest syslog|eventlog|<FILE>|console] [--noop]
53
+ [-l|--logdest syslog|eventlog|<ABS FILEPATH>|console] [--noop]
54
54
  [--catalog <catalog>] [--write-catalog-summary] <file>
55
55
 
56
56
 
@@ -31,4 +31,14 @@ class Puppet::Configurer::DownloaderFactory
31
31
  source_permissions
32
32
  )
33
33
  end
34
+
35
+ def create_locales_downloader(environment)
36
+ locales_downloader = Puppet::Configurer::Downloader.new(
37
+ "locales",
38
+ Puppet[:localedest],
39
+ Puppet[:localesource],
40
+ Puppet[:pluginsignore] + " config.yaml",
41
+ environment
42
+ )
43
+ end
34
44
  end