bolt 2.11.1 → 2.12.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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5e8e35ad928f8c0b182e0ce9457e5a33e0450da886f91b59637521fd72dc624
4
- data.tar.gz: d6b4c4844b91c6cfd8122422b37ebe567bb9a2be9557b3ceb3b3b266b599ca79
3
+ metadata.gz: c36dc3edc9637ac83eab0734e6143961ff7c97f4cbc0866ce8e1b5a0b5d318c2
4
+ data.tar.gz: c52ea03e1354361836c6d76c108735e2f8525aa5e8132f87cf895b5a0f2d802d
5
5
  SHA512:
6
- metadata.gz: 22b535ce7535ed5eb746c51f08794aa399f55be9e3b2e7ce83f1a38650f23902808245fe4162c033d3024a21dd1a1dd452e341eb4de34ebe9b2872b71ea17e59
7
- data.tar.gz: dc9ec1c71c2509dcabe92da46b3432d2b2c1e42b313a5dc274afc665bf739dd6b6582634ec5c5367b5dc6835cdbb387840344c20405249e304d8b43681ee9ac0
6
+ metadata.gz: ded5edab2a28a3a50d2eb9c9d2a1ccad43eae0272b09a609b219a78516bd32f8bff0e35751a95629d4d918cf3a22883d47a90c5c0f402c5aeedaffaab5dfc546
7
+ data.tar.gz: acde1204c706c49926a33b074da9b5262d1e6d11d27137bec55d7ea80a3ec7e009a1defc309e1f9a7fe824db9c0d4ce92963ddc6d3ae3d2619550fc5c4164ed3
@@ -109,6 +109,15 @@ Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction
109
109
  end
110
110
  end
111
111
 
112
+ # Wrap Sensitive parameters for plans that are run from the CLI, as it's impossible to pass
113
+ # a Sensitive value that way. We don't do this for plans run from the run_plan function, as
114
+ # it can receive Sensitive values as arguments.
115
+ # This should only happen after expanding target params, otherwise things will blow up if
116
+ # the targets are wrapped as Sensitive. Hopefully nobody does that, though...
117
+ if options[:bolt_api_call]
118
+ params = wrap_sensitive_parameters(params, closure.parameters)
119
+ end
120
+
112
121
  # wrap plan execution in logging messages
113
122
  executor.log_plan(plan_name) do
114
123
  result = nil
@@ -169,6 +178,58 @@ Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction
169
178
  end
170
179
  end
171
180
 
181
+ # Wrap any Sensitive parameters in the Sensitive wrapper type, unless they are already
182
+ # wrapped as Sensitive. This will also raise a helpful warning if the type expression
183
+ # is a complex data type using Sensitive, as we don't handle those cases.
184
+ def wrap_sensitive_parameters(params, param_models)
185
+ models = param_models.each_with_object({}) { |param, acc| acc[param.name] = param }
186
+
187
+ params.each_with_object({}) do |(name, value), acc|
188
+ model = models[name]
189
+
190
+ if sensitive_type?(model.type_expr)
191
+ acc[name] = Puppet::Pops::Types::PSensitiveType::Sensitive.new(value)
192
+ else
193
+ if model.type_expr.to_s.include?('Sensitive')
194
+ # Include the location for regular plans. YAML plans don't have this info, so
195
+ # the location will be suppressed.
196
+ file = defined?(model.file) ? model.file : :default
197
+ line = defined?(model.line) ? model.line : :default
198
+
199
+ Puppet.warn_once(
200
+ 'unsupported_sensitive_type',
201
+ name,
202
+ "Parameter '#{name}' is a complex type using Sensitive, unable to automatically wrap as Sensitive",
203
+ file,
204
+ line
205
+ )
206
+ end
207
+
208
+ acc[name] = value
209
+ end
210
+ end
211
+ end
212
+
213
+ # Whether the type is a supported Sensitive type. We only support wrapping parameterized
214
+ # and non-parameterized Sensitive types (e.g. Sensitive, Sensitive[String])
215
+ def sensitive_type?(type_expr)
216
+ # Parameterized Sensitive type (e.g. Sensitive[String])
217
+ # left_expr is defined whenever the type is parameterized. If this is a parameterized
218
+ # Sensitive type, then we check the cased_value, which is the stringified version of
219
+ # the left expression's type.
220
+ (defined?(type_expr.left_expr) && type_expr.left_expr.cased_value == 'Sensitive') ||
221
+ # Non-parameterized Sensitive type (Sensitive)
222
+ # cased_value is defined whenever the type is non-parameterized. If the type expression
223
+ # defines cased_value, then this is a simple type and we just need to check that it's
224
+ # Sensitive.
225
+ (defined?(type_expr.cased_value) && type_expr.cased_value == 'Sensitive') ||
226
+ # Sensitive type from YAML plans
227
+ # Type expressions from YAML plans are a different class than those from regular plans.
228
+ # As long as the type expression is PSensitiveType we can be sure that the type is
229
+ # either a parameterized or non-parameterized Sensitive type.
230
+ type_expr.instance_of?(Puppet::Pops::Types::PSensitiveType)
231
+ end
232
+
172
233
  def targets_to_param(targets, params, param_types)
173
234
  nodes_param = param_types.include?('nodes')
174
235
  targets_param = param_types['targets']&.any? { |p| p.match?(/TargetSpec/) }
@@ -29,7 +29,7 @@ module Bolt
29
29
  def self.build_client
30
30
  logger = Logging.logger[self]
31
31
  begin
32
- config_file = File.expand_path('~/.puppetlabs/bolt/analytics.yaml')
32
+ config_file = config_path(logger)
33
33
  config = load_config(config_file, logger)
34
34
  rescue ArgumentError
35
35
  config = { 'disabled' => true }
@@ -51,6 +51,25 @@ module Bolt
51
51
  NoopClient.new
52
52
  end
53
53
 
54
+ def self.config_path(logger)
55
+ path = File.expand_path(File.join('~', '.puppetlabs', 'etc', 'bolt', 'analytics.yaml'))
56
+ old_path = File.expand_path(File.join('~', '.puppetlabs', 'bolt', 'analytics.yaml'))
57
+
58
+ if File.exist?(path)
59
+ if File.exist?(old_path)
60
+ message = "Detected analytics configuration files at '#{old_path}' and '#{path}'. Loading "\
61
+ "analytics configuration from '#{path}'."
62
+ logger.warn(message)
63
+ end
64
+
65
+ path
66
+ elsif File.exist?(old_path)
67
+ old_path
68
+ else
69
+ path
70
+ end
71
+ end
72
+
54
73
  def self.load_config(filename, logger)
55
74
  if File.exist?(filename)
56
75
  YAML.load_file(filename)
@@ -59,7 +78,7 @@ module Bolt
59
78
  logger.warn <<~ANALYTICS
60
79
  Bolt collects data about how you use it. You can opt out of providing this data.
61
80
 
62
- To disable analytics data collection, add this line to ~/.puppetlabs/bolt/analytics.yaml :
81
+ To disable analytics data collection, add this line to ~/.puppetlabs/etc/bolt/analytics.yaml :
63
82
  disabled: true
64
83
 
65
84
  Read more about what data Bolt collects and why here:
@@ -20,7 +20,7 @@ module Bolt
20
20
  def get_help_text(subcommand, action = nil)
21
21
  case subcommand
22
22
  when 'apply'
23
- { flags: ACTION_OPTS + %w[noop execute compile-concurrency],
23
+ { flags: ACTION_OPTS + %w[noop execute compile-concurrency hiera-config],
24
24
  banner: APPLY_HELP }
25
25
  when 'command'
26
26
  case action
@@ -172,13 +172,14 @@ module Bolt
172
172
  apply
173
173
 
174
174
  USAGE
175
- bolt apply <manifest.pp> [options]
175
+ bolt apply [manifest.pp] [options]
176
176
 
177
177
  DESCRIPTION
178
178
  Apply Puppet manifest code on the specified targets.
179
179
 
180
180
  EXAMPLES
181
- bolt apply manifest.pp --targets target1,target2
181
+ bolt apply manifest.pp -t target
182
+ bolt apply -e "file { '/etc/puppetlabs': ensure => present }" -t target
182
183
  HELP
183
184
 
184
185
  COMMAND_HELP = <<~HELP
@@ -537,6 +537,17 @@ module Bolt
537
537
  Puppet[:tasks] = false
538
538
  ast = pal.parse_manifest(code, filename)
539
539
 
540
+ if defined?(ast.body) &&
541
+ (ast.body.is_a?(Puppet::Pops::Model::HostClassDefinition) ||
542
+ ast.body.is_a?(Puppet::Pops::Model::ResourceTypeDefinition))
543
+ message = "Manifest only contains definitions and will result in no changes on the targets. "\
544
+ "Definitions must be declared for their resources to be applied. You can read more "\
545
+ "about defining and declaring classes and types in the Puppet documentation at "\
546
+ "https://puppet.com/docs/puppet/latest/lang_classes.html and "\
547
+ "https://puppet.com/docs/puppet/latest/lang_defined_types.html"
548
+ @logger.warn(message)
549
+ end
550
+
540
551
  executor = Bolt::Executor.new(config.concurrency, analytics, noop)
541
552
  executor.subscribe(outputter) if options.fetch(:format, 'human') == 'human'
542
553
  executor.subscribe(log_outputter)
@@ -115,7 +115,7 @@ module Bolt
115
115
  data: Bolt::Util.read_optional_yaml_hash(project.config_file, 'config')
116
116
  }
117
117
 
118
- data = load_defaults.push(data).select { |config| config[:data]&.any? }
118
+ data = load_defaults(project).push(data).select { |config| config[:data]&.any? }
119
119
 
120
120
  new(project, data, overrides)
121
121
  end
@@ -127,27 +127,29 @@ module Bolt
127
127
  filepath: project.config_file,
128
128
  data: Bolt::Util.read_yaml_hash(configfile, 'config')
129
129
  }
130
- data = load_defaults.push(data).select { |config| config[:data]&.any? }
130
+ data = load_defaults(project).push(data).select { |config| config[:data]&.any? }
131
131
 
132
132
  new(project, data, overrides)
133
133
  end
134
134
 
135
- def self.load_defaults
135
+ def self.load_defaults(project)
136
136
  # Lazy-load expensive gem code
137
137
  require 'win32/dir' if Bolt::Util.windows?
138
138
 
139
- system_path = if Bolt::Util.windows?
140
- Pathname.new(File.join(Dir::COMMON_APPDATA, 'PuppetLabs', 'bolt', 'etc', 'bolt.yaml'))
141
- else
142
- Pathname.new(File.join('/etc', 'puppetlabs', 'bolt', 'bolt.yaml'))
143
- end
139
+ # Don't load /etc/puppetlabs/bolt/bolt.yaml twice
140
+ confs = if project.path == Bolt::Project.system_path
141
+ []
142
+ else
143
+ system_path = Pathname.new(File.join(Bolt::Project.system_path, 'bolt.yaml'))
144
+ [{ filepath: system_path, data: Bolt::Util.read_optional_yaml_hash(system_path, 'config') }]
145
+ end
146
+
144
147
  user_path = begin
145
148
  Pathname.new(File.expand_path(File.join('~', '.puppetlabs', 'etc', 'bolt', 'bolt.yaml')))
146
149
  rescue ArgumentError
147
150
  nil
148
151
  end
149
152
 
150
- confs = [{ filepath: system_path, data: Bolt::Util.read_optional_yaml_hash(system_path, 'config') }]
151
153
  confs << { filepath: user_path, data: Bolt::Util.read_optional_yaml_hash(user_path, 'config') } if user_path
152
154
  confs
153
155
  end
@@ -359,6 +359,7 @@ module Bolt
359
359
  name = param.name
360
360
  if signature_params.include?(name)
361
361
  params[name] = { 'type' => param.types.first }
362
+ params[name]['sensitive'] = param.types.first =~ /\ASensitive(\[.*\])?\z/ ? true : false
362
363
  params[name]['default_value'] = defaults[name] if defaults.key?(name)
363
364
  params[name]['description'] = param.text unless param.text.empty?
364
365
  else
@@ -390,6 +391,7 @@ module Bolt
390
391
  param.type_expr
391
392
  end
392
393
  params[name] = { 'type' => type_str }
394
+ params[name]['sensitive'] = param.type_expr.instance_of?(Puppet::Pops::Types::PSensitiveType)
393
395
  params[name]['default_value'] = param.value
394
396
  params[name]['description'] = param.description if param.description
395
397
  end
@@ -16,7 +16,18 @@ module Bolt
16
16
  :puppetfile, :rerunfile, :type, :resource_types
17
17
 
18
18
  def self.default_project
19
- Project.new(File.join('~', '.puppetlabs', 'bolt'), 'user')
19
+ Project.new(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user')
20
+ # If homedir isn't defined use the system config path
21
+ rescue ArgumentError
22
+ Project.new(system_path, 'system')
23
+ end
24
+
25
+ def self.system_path
26
+ if Bolt::Util.windows?
27
+ File.join(Dir::COMMON_APPDATA, 'PuppetLabs', 'bolt', 'etc')
28
+ else
29
+ File.join('/etc', 'puppetlabs', 'bolt')
30
+ end
20
31
  end
21
32
 
22
33
  # Search recursively up the directory hierarchy for the Project. Look for a
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '2.11.1'
4
+ VERSION = '2.12.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.11.1
4
+ version: 2.12.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-05-28 00:00:00.000000000 Z
11
+ date: 2020-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable