bolt 0.18.0 → 0.18.1

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9b5850dae92768743f9faa35a5972efc6c3b83da
4
- data.tar.gz: 80c524fd01e4b66c1d969e1c98fe29bbf836369d
3
+ metadata.gz: 0658ba25967cd7fac93e3b0c410793f2d389849f
4
+ data.tar.gz: f7a6cafb673449de219fb9bade1e672d4ff858eb
5
5
  SHA512:
6
- metadata.gz: 50ee00db02f62fafeb43d1553186442728db8dfc8d69ec646fd284415b6595c78445a95435d90fd894071ce5c7c39ec3d0f06dbb52337be648e237ebebb97677
7
- data.tar.gz: bc78146c6b867e02c70fb4ea612c498a6ac1260917c490a31c0435a4978a1777fc763f34013e31d65d88dedb537a647ed2656a6a92a7d3ede3af13191b48f256
6
+ metadata.gz: 3908b25652b38dd0a7e48656b3442fe6b5e85fd23942dbb2be207f4f4f39397b18d7b87706791075773b53c0d297d0b9ba8743a0070c5bcde8d8d7be11a71ce8
7
+ data.tar.gz: e837918752a7e2cc2ac2bff3aa72f480c4fcf7f498fca15cf355a9cb2b7bc24faa2e91b1684e7650ead33a06315961ceb6792148b70cff974bc0e960972454ca
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/error'
4
+
5
+ # Returns a hash of certname to facts objects for each matched Target. This
6
+ # functions accepts an array of certnames and returns a hash of target
7
+ # certnames and their corresponding facts hash.
8
+ #
9
+ # * If a node is not found in PuppetDB, it's included in the returned hash with empty facts hash.
10
+ # * Otherwise the node is included in the hash with a value that is a hash of it's facts.
11
+ #
12
+ Puppet::Functions.create_function(:puppetdb_fact) do
13
+ dispatch :puppetdb_fact do
14
+ param 'Array[String]', :targets
15
+ end
16
+
17
+ def puppetdb_fact(targets)
18
+ unless Puppet[:tasks]
19
+ raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
20
+ Puppet::Pops::Issues::TASK_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, operation: 'puppetdb_fact'
21
+ )
22
+ end
23
+
24
+ executor = Puppet.lookup(:bolt_executor) { nil }
25
+ executor.puppetdb_fact(targets)
26
+ end
27
+ end
data/lib/bolt/cli.rb CHANGED
@@ -116,20 +116,15 @@ Available options are:
116
116
  @options = options
117
117
 
118
118
  @nodes = define('-n', '--nodes NODES',
119
- 'Node(s) to connect to in URI format [protocol://]host[:port] (Optional)',
120
- 'Eg. --nodes bolt.puppet.com',
121
- 'Eg. --nodes localhost,ssh://nix.com:2222,winrm://windows.puppet.com',
122
- # An empty line in a switch description causes the OptionParser#help
123
- # method to raise an exception. Specifying a line containing only a
124
- # newline is the closest one can get to the empty line without
125
- # triggering that exception.
126
- "\n",
127
- '* NODES can either be comma-separated, \'@<file>\' to read',
128
- '* nodes from a file, or \'-\' to read from stdin',
129
- '* Windows nodes must specify protocol with winrm://',
130
- '* protocol is `ssh` by default, may be `ssh` or `winrm`',
131
- '* port defaults to `22` for SSH',
132
- '* port defaults to `5985` or `5986` for WinRM, based on the --[no-]ssl setting') do |nodes|
119
+ 'Identifies the nodes to target.',
120
+ 'Enter a comma-separated list of node URIs or group names.',
121
+ "Or read a node list from an input file '@<file>' or stdin '-'.",
122
+ 'Example: --nodes localhost,node_group,ssh://nix.com:2222,winrm://windows.puppet.com',
123
+ 'URI format is [protocol://]host[:port]',
124
+ "SSH is the default protocol; may be #{TRANSPORTS.keys.join(', ')}",
125
+ 'For Windows nodes, specify the winrm:// protocol if it has not be configured',
126
+ 'For SSH, port defaults to `22`',
127
+ 'For WinRM, port defaults to `5985` or `5986` based on the --[no-]ssl setting') do |nodes|
133
128
  @options[:nodes] << get_arg_input(nodes)
134
129
  end.extend(SwitchHider)
135
130
  @query = define('-q', '--query QUERY',
@@ -137,11 +132,11 @@ Available options are:
137
132
  @options[:query] = query
138
133
  end.extend(SwitchHider)
139
134
  define('-u', '--user USER',
140
- 'User to authenticate as (Optional)') do |user|
135
+ 'User to authenticate as') do |user|
141
136
  @options[:user] = user
142
137
  end
143
138
  define('-p', '--password [PASSWORD]',
144
- 'Password to authenticate with (Optional).',
139
+ 'Password to authenticate with.',
145
140
  'Omit the value to prompt for the password.') do |password|
146
141
  if password.nil?
147
142
  STDOUT.print "Please enter your password: "
@@ -152,25 +147,25 @@ Available options are:
152
147
  end
153
148
  end
154
149
  define('--private-key KEY',
155
- 'Private ssh key to authenticate with (Optional)') do |key|
150
+ 'Private ssh key to authenticate with') do |key|
156
151
  @options[:'private-key'] = key
157
152
  end
158
153
  define('--tmpdir DIR',
159
- 'The directory to upload and execute temporary files on the target (Optional)') do |tmpdir|
154
+ 'The directory to upload and execute temporary files on the target') do |tmpdir|
160
155
  @options[:tmpdir] = tmpdir
161
156
  end
162
157
  define('-c', '--concurrency CONCURRENCY', Integer,
163
158
  'Maximum number of simultaneous connections ' \
164
- '(Optional, defaults to 100)') do |concurrency|
159
+ '(defaults to 100)') do |concurrency|
165
160
  @options[:concurrency] = concurrency
166
161
  end
167
162
  define('--connect-timeout TIMEOUT', Integer,
168
- 'Connection timeout (Optional)') do |timeout|
163
+ 'Connection timeout (defaults vary)') do |timeout|
169
164
  @options[:'connect-timeout'] = timeout
170
165
  end
171
166
  define('--modulepath MODULES',
172
167
  'List of directories containing modules, ' \
173
- "separated by #{File::PATH_SEPARATOR}") do |modulepath|
168
+ "separated by '#{File::PATH_SEPARATOR}'") do |modulepath|
174
169
  @options[:modulepath] = modulepath.split(File::PATH_SEPARATOR)
175
170
  end
176
171
  define('--params PARAMETERS',
data/lib/bolt/error.rb CHANGED
@@ -63,4 +63,14 @@ module Bolt
63
63
  @error_code = 2
64
64
  end
65
65
  end
66
+
67
+ class PuppetError < Error
68
+ def self.convert_puppet_errors(result)
69
+ Bolt::Util.walk_vals(result) { |v| v.is_a?(Puppet::DataTypes::Error) ? from_error(v) : v }
70
+ end
71
+
72
+ def self.from_error(err)
73
+ new(err.msg, err.kind, err.details, err.issue_code)
74
+ end
75
+ end
66
76
  end
data/lib/bolt/executor.rb CHANGED
@@ -9,6 +9,7 @@ require 'bolt/result'
9
9
  require 'bolt/config'
10
10
  require 'bolt/notifier'
11
11
  require 'bolt/result_set'
12
+ require 'bolt/puppetdb'
12
13
 
13
14
  module Bolt
14
15
  class Executor
@@ -154,5 +155,17 @@ module Bolt
154
155
  @notifier.shutdown
155
156
  results
156
157
  end
158
+
159
+ def puppetdb_client
160
+ return @puppetdb_client if @puppetdb_client
161
+ puppetdb_config = Bolt::PuppetDB::Config.new(nil, @config.puppetdb)
162
+ @puppetdb_client = Bolt::PuppetDB::Client.from_config(puppetdb_config)
163
+ end
164
+
165
+ def puppetdb_fact(certnames)
166
+ puppetdb_client.facts_for_node(certnames)
167
+ rescue StandardError => e
168
+ raise Bolt::CLIError, "Could not retrieve targets from PuppetDB: #{e}"
169
+ end
157
170
  end
158
171
  end
@@ -93,7 +93,7 @@ module Bolt
93
93
  end
94
94
 
95
95
  def vars(target)
96
- @target_vars[target.name]
96
+ @target_vars[target.name] || {}
97
97
  end
98
98
 
99
99
  def add_facts(target, new_facts = {})
@@ -102,7 +102,7 @@ module Bolt
102
102
  end
103
103
 
104
104
  def facts(target)
105
- @target_facts[target.name]
105
+ @target_facts[target.name] || {}
106
106
  end
107
107
 
108
108
  #### PRIVATE ####
data/lib/bolt/pal.rb CHANGED
@@ -41,14 +41,6 @@ module Bolt
41
41
  # Now that puppet is loaded we can include puppet mixins in data types
42
42
  Bolt::ResultSet.include_iterable
43
43
 
44
- # TODO: This is a hack for PUP-8441 remove it once that is fixed
45
- require_relative '../../vendored/puppet/lib/puppet/datatypes/impl/error.rb'
46
- Puppet::DataTypes::Error.class_eval do
47
- def to_json(opts = nil)
48
- _pcore_init_hash.to_json(opts)
49
- end
50
- end
51
-
52
44
  unless Puppet.settings.global_defaults_initialized?
53
45
  Puppet.initialize_settings
54
46
  end
@@ -99,7 +91,8 @@ module Bolt
99
91
  end
100
92
  end
101
93
 
102
- if r.is_a? StandardError
94
+ # Plans may return PuppetError but nothing should be throwing them
95
+ if r.is_a?(StandardError) && !r.is_a?(Bolt::PuppetError)
103
96
  raise r
104
97
  end
105
98
  r
@@ -153,7 +146,7 @@ module Bolt
153
146
  def parse_params(type, object_name, params)
154
147
  in_bolt_compiler do |compiler|
155
148
  if type == 'task'
156
- param_spec = compiler.task_signature(object_name)&.task_hash&.dig('parameters')
149
+ param_spec = compiler.task_signature(object_name)&.task_hash
157
150
  elsif type == 'plan'
158
151
  plan = compiler.plan_signature(object_name)
159
152
  param_spec = plan_hash(object_name, plan) if plan
@@ -161,7 +154,7 @@ module Bolt
161
154
  param_spec ||= {}
162
155
 
163
156
  params.each_with_object({}) do |(name, str), acc|
164
- type = param_spec.dig(name, 'type')
157
+ type = param_spec.dig('parameters', name, 'type')
165
158
  begin
166
159
  parsed = JSON.parse(str, quirks_mode: true)
167
160
  # The type may not exist if the module is remote on orch or if a task
@@ -199,10 +192,11 @@ module Bolt
199
192
  end
200
193
  end
201
194
 
202
- # This coverts a plan signature object into a format approximating
203
- # the task_hash of a task_signature
195
+ # This converts a plan signature object into a format approximating the
196
+ # task_hash of a task_signature. Must be called from within bolt compiler
197
+ # to pickup type aliases used in the plan signature.
204
198
  def plan_hash(plan_name, plan)
205
- elements = plan.params_type.elements || {}
199
+ elements = plan.params_type.elements || []
206
200
  parameters = elements.each_with_object({}) do |param, acc|
207
201
  acc[param.name] = { 'type' => param.value_type }
208
202
  acc[param.name]['default_value'] = nil if param.key_type.is_a?(Puppet::Pops::Types::POptionalType)
@@ -214,14 +208,15 @@ module Bolt
214
208
  end
215
209
 
216
210
  def get_plan_info(plan_name)
217
- plan = in_bolt_compiler do |compiler|
218
- compiler.plan_signature(plan_name)
211
+ plan_info = in_bolt_compiler do |compiler|
212
+ plan = compiler.plan_signature(plan_name)
213
+ plan_hash(plan_name, plan) if plan
219
214
  end
220
215
 
221
- if plan.nil?
216
+ if plan_info.nil?
222
217
  raise Bolt::CLIError, Bolt::Error.unknown_plan(plan_name)
223
218
  end
224
- plan_hash(plan_name, plan)
219
+ plan_info
225
220
  end
226
221
 
227
222
  def run_task(task_name, targets, params, executor, inventory, &eventblock)
@@ -232,7 +227,8 @@ module Bolt
232
227
 
233
228
  def run_plan(plan_name, params, executor = nil, inventory = nil)
234
229
  in_plan_compiler(executor, inventory) do |compiler|
235
- compiler.call_function('run_plan', plan_name, params)
230
+ r = compiler.call_function('run_plan', plan_name, params)
231
+ Bolt::PuppetError.convert_puppet_errors(r)
236
232
  end
237
233
  end
238
234
  end
@@ -51,6 +51,33 @@ module Bolt
51
51
  end
52
52
  end
53
53
 
54
+ # This method expects an array of certnames to get facts for
55
+ def facts_for_node(certnames)
56
+ return {} if certnames.empty? || certnames.nil?
57
+
58
+ # This inits a hash of {certname1 => {}, certname2 => {}} etc.
59
+ output = Hash[certnames.product([{}])]
60
+
61
+ certnames.uniq!
62
+ name_query = certnames.map { |c| ["=", "certname", c] }
63
+ name_query.insert(0, "or")
64
+ body = JSON.generate(query: name_query)
65
+
66
+ response = http_client.post("#{@uri}/pdb/query/v4/facts", body: body, header: headers)
67
+ if response.code != 200
68
+ raise Bolt::PuppetDBError, "Failed to query PuppetDB: #{response.body}"
69
+ else
70
+ parsed = JSON.parse(response.body)
71
+ end
72
+
73
+ # Parse the data
74
+ parsed&.each do |f|
75
+ output[f['certname']][f['name']] = f['value']
76
+ end
77
+
78
+ output
79
+ end
80
+
54
81
  def http_client
55
82
  return @http if @http
56
83
  @http = HTTPClient.new
@@ -32,8 +32,6 @@ module Bolt
32
32
  yield dir
33
33
  end
34
34
  end
35
- rescue StandardError => e
36
- raise Bolt::Node::FileError.new("Could not make tempdir: #{e.message}", 'TEMPDIR_ERROR')
37
35
  end
38
36
  private :in_tmpdir
39
37
 
@@ -9,7 +9,7 @@ module Bolt
9
9
  class Shell
10
10
  def execute(*command, options)
11
11
  if options[:env]
12
- env = options[:env].each_with_object({}) { |(k, v), h| h["PT_#{k}"] = v }
12
+ env = options[:env].each_with_object({}) { |(k, v), h| h["PT_#{k}"] = v.to_s }
13
13
  command = [env] + command
14
14
  end
15
15
 
data/lib/bolt/util.rb CHANGED
@@ -46,7 +46,7 @@ module Bolt
46
46
  end
47
47
 
48
48
  # Accepts a Data object and returns a copy with all hash keys
49
- # modifed by block. use &:to_s to stringify keys or :to_sym to symbolize them
49
+ # modified by block. use &:to_s to stringify keys or &:to_sym to symbolize them
50
50
  def walk_keys(data, &block)
51
51
  if data.is_a? Hash
52
52
  data.each_with_object({}) do |(k, v), acc|
@@ -60,6 +60,20 @@ module Bolt
60
60
  end
61
61
  end
62
62
 
63
+ # Accepts a Data object and returns a copy with all hash and array values
64
+ # Arrays and hashes including the initial object are modified before
65
+ # their descendants are.
66
+ def walk_vals(data, &block)
67
+ data = yield(data)
68
+ if data.is_a? Hash
69
+ map_vals(data) { |v| walk_vals(v, &block) }
70
+ elsif data.is_a? Array
71
+ data.map { |v| walk_vals(v, &block) }
72
+ else
73
+ data
74
+ end
75
+ end
76
+
63
77
  # Performs a deep_clone, using an identical copy if the cloned structure contains multiple
64
78
  # references to the same object and prevents endless recursion.
65
79
  # Credit to Jan Molic via https://github.com/rubyworks/facets/blob/master/LICENSE.txt
data/lib/bolt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '0.18.0'
4
+ VERSION = '0.18.1'
5
5
  end
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'bolt/puppetdb/config'
4
+ require 'bolt/puppetdb'
5
5
  require 'json'
6
6
  require 'httpclient'
7
7
  require 'optparse'
@@ -0,0 +1,10 @@
1
+ plan puppetdb_fact(TargetSpec $nodes) {
2
+ $targets = get_targets($nodes)
3
+ $certnames = $targets.map |$target| { $target.host }
4
+ $pdb_facts = puppetdb_fact($certnames)
5
+ $targets.each |$target| {
6
+ add_facts($target, $pdb_facts[$target.host])
7
+ }
8
+
9
+ return $pdb_facts
10
+ }
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: 0.18.0
4
+ version: 0.18.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-03-22 00:00:00.000000000 Z
11
+ date: 2018-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -327,6 +327,7 @@ files:
327
327
  - bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb
328
328
  - bolt-modules/boltlib/lib/puppet/functions/file_upload.rb
329
329
  - bolt-modules/boltlib/lib/puppet/functions/get_targets.rb
330
+ - bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb
330
331
  - bolt-modules/boltlib/lib/puppet/functions/run_command.rb
331
332
  - bolt-modules/boltlib/lib/puppet/functions/run_plan.rb
332
333
  - bolt-modules/boltlib/lib/puppet/functions/run_script.rb
@@ -376,6 +377,7 @@ files:
376
377
  - modules/canary/lib/puppet/functions/canary/random_split.rb
377
378
  - modules/canary/lib/puppet/functions/canary/skip.rb
378
379
  - modules/canary/plans/init.pp
380
+ - modules/puppetdb_fact/plans/init.pp
379
381
  - vendored/facter/lib/facter.rb
380
382
  - vendored/facter/lib/facter/Cfkey.rb
381
383
  - vendored/facter/lib/facter/application.rb