bolt 2.30.0 → 2.34.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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +12 -12
  3. data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +1 -1
  4. data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +6 -0
  5. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +2 -2
  6. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +1 -1
  7. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +1 -1
  8. data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +1 -1
  9. data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +1 -1
  10. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
  11. data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +2 -2
  12. data/bolt-modules/out/lib/puppet/functions/out/message.rb +44 -1
  13. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +3 -0
  14. data/guides/logging.txt +18 -0
  15. data/guides/module.txt +19 -0
  16. data/guides/modulepath.txt +25 -0
  17. data/lib/bolt/bolt_option_parser.rb +6 -1
  18. data/lib/bolt/cli.rb +82 -142
  19. data/lib/bolt/config/modulepath.rb +30 -0
  20. data/lib/bolt/config/options.rb +31 -13
  21. data/lib/bolt/config/transport/options.rb +2 -2
  22. data/lib/bolt/error.rb +13 -3
  23. data/lib/bolt/executor.rb +24 -12
  24. data/lib/bolt/inventory.rb +10 -9
  25. data/lib/bolt/inventory/group.rb +2 -1
  26. data/lib/bolt/module_installer.rb +117 -91
  27. data/lib/bolt/{puppetfile → module_installer}/installer.rb +3 -2
  28. data/lib/bolt/module_installer/puppetfile.rb +117 -0
  29. data/lib/bolt/module_installer/puppetfile/forge_module.rb +54 -0
  30. data/lib/bolt/module_installer/puppetfile/git_module.rb +37 -0
  31. data/lib/bolt/module_installer/puppetfile/module.rb +26 -0
  32. data/lib/bolt/module_installer/resolver.rb +76 -0
  33. data/lib/bolt/module_installer/specs.rb +93 -0
  34. data/lib/bolt/module_installer/specs/forge_spec.rb +85 -0
  35. data/lib/bolt/module_installer/specs/git_spec.rb +179 -0
  36. data/lib/bolt/outputter.rb +0 -47
  37. data/lib/bolt/outputter/human.rb +46 -16
  38. data/lib/bolt/outputter/json.rb +17 -8
  39. data/lib/bolt/pal.rb +52 -40
  40. data/lib/bolt/pal/yaml_plan.rb +4 -2
  41. data/lib/bolt/pal/yaml_plan/evaluator.rb +23 -1
  42. data/lib/bolt/pal/yaml_plan/loader.rb +14 -9
  43. data/lib/bolt/plan_creator.rb +160 -0
  44. data/lib/bolt/plugin.rb +2 -2
  45. data/lib/bolt/project.rb +6 -11
  46. data/lib/bolt/project_migrator.rb +1 -1
  47. data/lib/bolt/project_migrator/base.rb +2 -2
  48. data/lib/bolt/project_migrator/config.rb +5 -4
  49. data/lib/bolt/project_migrator/inventory.rb +3 -3
  50. data/lib/bolt/project_migrator/modules.rb +23 -21
  51. data/lib/bolt/puppetdb/config.rb +5 -5
  52. data/lib/bolt/result.rb +23 -11
  53. data/lib/bolt/shell/bash.rb +14 -8
  54. data/lib/bolt/shell/powershell.rb +12 -7
  55. data/lib/bolt/task/run.rb +1 -1
  56. data/lib/bolt/transport/base.rb +18 -18
  57. data/lib/bolt/transport/docker.rb +23 -6
  58. data/lib/bolt/transport/orch.rb +26 -17
  59. data/lib/bolt/transport/remote.rb +3 -3
  60. data/lib/bolt/transport/simple.rb +6 -6
  61. data/lib/bolt/transport/ssh/connection.rb +1 -1
  62. data/lib/bolt/util.rb +5 -0
  63. data/lib/bolt/version.rb +1 -1
  64. data/lib/bolt_server/file_cache.rb +2 -0
  65. data/lib/bolt_server/schemas/partials/task.json +17 -2
  66. data/lib/bolt_server/transport_app.rb +92 -12
  67. data/lib/bolt_spec/bolt_context.rb +4 -2
  68. data/lib/bolt_spec/plans.rb +1 -1
  69. data/lib/bolt_spec/plans/action_stubs/command_stub.rb +1 -1
  70. data/lib/bolt_spec/plans/action_stubs/script_stub.rb +1 -1
  71. data/lib/bolt_spec/plans/mock_executor.rb +5 -5
  72. data/lib/bolt_spec/run.rb +1 -1
  73. metadata +24 -9
  74. data/lib/bolt/puppetfile.rb +0 -142
  75. data/lib/bolt/puppetfile/module.rb +0 -90
  76. data/lib/bolt_server/pe/pal.rb +0 -67
  77. data/modules/secure_env_vars/plans/init.pp +0 -20
@@ -14,7 +14,9 @@ require 'json-schema'
14
14
 
15
15
  # These are only needed for the `/plans` endpoint.
16
16
  require 'puppet'
17
- require 'bolt_server/pe/pal'
17
+
18
+ # Needed by the `/project_file_metadatas` endpoint
19
+ require 'puppet/file_serving/fileset'
18
20
 
19
21
  module BoltServer
20
22
  class TransportApp < Sinatra::Base
@@ -35,6 +37,17 @@ module BoltServer
35
37
  transport-winrm
36
38
  ].freeze
37
39
 
40
+ # PE_BOLTLIB_PATH is intended to function exactly like the BOLTLIB_PATH used
41
+ # in Bolt::PAL. Paths and variable names are similar to what exists in
42
+ # Bolt::PAL, but with a 'PE' prefix.
43
+ PE_BOLTLIB_PATH = '/opt/puppetlabs/server/apps/bolt-server/pe-bolt-modules'
44
+
45
+ # For now at least, we maintain an entirely separate codedir from
46
+ # puppetserver by default, so that filesync can work properly. If filesync
47
+ # is not used, this can instead match the usual puppetserver codedir.
48
+ # See the `orchestrator.bolt.codedir` tk config setting.
49
+ DEFAULT_BOLT_CODEDIR = '/opt/puppetlabs/server/data/orchestration-services/code'
50
+
38
51
  def initialize(config)
39
52
  @config = config
40
53
  @schemas = Hash[REQUEST_SCHEMAS.map do |basename|
@@ -191,10 +204,52 @@ module BoltServer
191
204
  [@executor.run_script(target, file_location, body['arguments'])]
192
205
  end
193
206
 
207
+ # This function is nearly identical to Bolt::Pal's `with_puppet_settings` with the
208
+ # one difference that we set the codedir to point to actual code, rather than the
209
+ # tmpdir. We only use this funtion inside the Modulepath initializer so that Puppet
210
+ # is correctly configured to pull environment configuration correctly. If we don't
211
+ # set codedir in this way: when we try to load and interpolate the modulepath it
212
+ # won't correctly load.
213
+ #
214
+ # WARNING: THIS FUNCTION SHOULD ONLY BE CALLED INSIDE A SYNCHRONIZED PAL MUTEX
215
+ def with_pe_pal_init_settings(codedir, environmentpath, basemodulepath)
216
+ Dir.mktmpdir('pe-bolt') do |dir|
217
+ cli = []
218
+ Puppet::Settings::REQUIRED_APP_SETTINGS.each do |setting|
219
+ dir = setting == :codedir ? codedir : dir
220
+ cli << "--#{setting}" << dir
221
+ end
222
+ cli << "--environmentpath" << environmentpath
223
+ cli << "--basemodulepath" << basemodulepath
224
+ Puppet.settings.send(:clear_everything_for_tests)
225
+ Puppet.initialize_settings(cli)
226
+ yield
227
+ end
228
+ end
229
+
230
+ # Use puppet to identify the modulepath from an environment.
231
+ #
232
+ # WARNING: THIS FUNCTION SHOULD ONLY BE CALLED INSIDE A SYNCHRONIZED PAL MUTEX
233
+ def modulepath_from_environment(environment_name)
234
+ codedir = DEFAULT_BOLT_CODEDIR
235
+ environmentpath = "#{codedir}/environments"
236
+ basemodulepath = "#{codedir}/modules:/opt/puppetlabs/puppet/modules"
237
+ modulepath_dirs = nil
238
+ with_pe_pal_init_settings(codedir, environmentpath, basemodulepath) do
239
+ environment = Puppet.lookup(:environments).get!(environment_name)
240
+ modulepath_dirs = environment.modulepath
241
+ end
242
+ modulepath_dirs
243
+ end
244
+
194
245
  def in_pe_pal_env(environment)
195
246
  return [400, '`environment` is a required argument'] if environment.nil?
196
247
  @pal_mutex.synchronize do
197
- pal = BoltServer::PE::PAL.new({}, environment)
248
+ modulepath_obj = Bolt::Config::Modulepath.new(
249
+ modulepath_from_environment(environment),
250
+ boltlib_path: [PE_BOLTLIB_PATH, Bolt::Config::Modulepath::BOLTLIB_PATH]
251
+ )
252
+ pal = Bolt::PAL.new(modulepath_obj, nil, nil)
198
253
  yield pal
199
254
  rescue Puppet::Environments::EnvironmentNotFound
200
255
  [400, {
@@ -213,16 +268,11 @@ module BoltServer
213
268
  @pal_mutex.synchronize do
214
269
  project = Bolt::Project.create_project(project_dir)
215
270
  bolt_config = Bolt::Config.from_project(project, { log: { 'bolt-debug.log' => 'disable' } })
216
- pal = Bolt::PAL.new(bolt_config.modulepath, nil, nil, nil, nil, nil, bolt_config.project)
217
- module_path = [
218
- BoltServer::PE::PAL::PE_BOLTLIB_PATH,
219
- Bolt::PAL::BOLTLIB_PATH,
220
- *bolt_config.modulepath,
221
- Bolt::PAL::MODULES_PATH
222
- ]
223
- # CODEREVIEW: I *think* this is the only thing we need to make different between bolt's PAL. Is it acceptable
224
- # to hack this? Modulepath is currently a readable attribute, could we make it writeable?
225
- pal.instance_variable_set(:@modulepath, module_path)
271
+ modulepath_object = Bolt::Config::Modulepath.new(
272
+ bolt_config.modulepath,
273
+ boltlib_path: [PE_BOLTLIB_PATH, Bolt::Config::Modulepath::BOLTLIB_PATH]
274
+ )
275
+ pal = Bolt::PAL.new(modulepath_object, nil, nil, nil, nil, nil, bolt_config.project)
226
276
  context = {
227
277
  pal: pal,
228
278
  config: bolt_config
@@ -307,6 +357,23 @@ module BoltServer
307
357
  plans.map { |plan_name| { 'name' => plan_name } }
308
358
  end
309
359
 
360
+ def file_metadatas(pal, module_name, file)
361
+ pal.in_bolt_compiler do
362
+ mod = Puppet.lookup(:current_environment).module(module_name)
363
+ raise ArgumentError, "`module_name`: #{module_name} does not exist" unless mod
364
+ abs_file_path = mod.file(file)
365
+ raise ArgumentError, "`file`: #{file} does not exist inside the module's 'files' directory" unless abs_file_path
366
+ fileset = Puppet::FileServing::Fileset.new(abs_file_path, 'recurse' => 'yes')
367
+ Puppet::FileServing::Fileset.merge(fileset).collect do |relative_file_path, base_path|
368
+ metadata = Puppet::FileServing::Metadata.new(base_path, relative_path: relative_file_path)
369
+ metadata.checksum_type = 'sha256'
370
+ metadata.links = 'follow'
371
+ metadata.collect
372
+ metadata.to_data_hash
373
+ end
374
+ end
375
+ end
376
+
310
377
  get '/' do
311
378
  200
312
379
  end
@@ -550,6 +617,19 @@ module BoltServer
550
617
  end
551
618
  end
552
619
 
620
+ # Implements puppetserver's file_metadatas endpoint for projects.
621
+ #
622
+ # @param project_ref [String] the project_ref to fetch the file metadatas from
623
+ get '/project_file_metadatas/:module_name/*' do
624
+ in_bolt_project(params['project_ref']) do |context|
625
+ file = params[:splat].first
626
+ metadatas = file_metadatas(context[:pal], params[:module_name], file)
627
+ [200, metadatas.to_json]
628
+ end
629
+ rescue ArgumentError => e
630
+ [400, e.message]
631
+ end
632
+
553
633
  error 404 do
554
634
  err = Bolt::Error.new("Could not find route #{request.path}",
555
635
  'boltserver/not-found')
@@ -105,7 +105,7 @@ module BoltSpec
105
105
 
106
106
  # Set the things
107
107
  Puppet[:tasks] = true
108
- RSpec.configuration.module_path = [modulepath, Bolt::PAL::BOLTLIB_PATH].join(File::PATH_SEPARATOR)
108
+ RSpec.configuration.module_path = [modulepath, Bolt::Config::Modulepath::BOLTLIB_PATH].join(File::PATH_SEPARATOR)
109
109
  opts = {
110
110
  bolt_executor: executor,
111
111
  bolt_inventory: inventory,
@@ -153,7 +153,9 @@ module BoltSpec
153
153
  end
154
154
 
155
155
  def pal
156
- @pal ||= Bolt::PAL.new(config.modulepath, config.hiera_config, config.project.resource_types)
156
+ @pal ||= Bolt::PAL.new(Bolt::Config::Modulepath.new(config.modulepath),
157
+ config.hiera_config,
158
+ config.project.resource_types)
157
159
  end
158
160
 
159
161
  BoltSpec::Plans::MOCKED_ACTIONS.each do |action|
@@ -224,7 +224,7 @@ module BoltSpec
224
224
 
225
225
  def run_plan(name, params)
226
226
  pal = Bolt::PAL.new(
227
- config.modulepath,
227
+ Bolt::Config::Modulepath.new(config.modulepath),
228
228
  config.hiera_config,
229
229
  config.project.resource_types,
230
230
  config.compile_concurrency,
@@ -29,7 +29,7 @@ module BoltSpec
29
29
  end
30
30
 
31
31
  def result_for(target, stdout: '', stderr: '')
32
- Bolt::Result.for_command(target, stdout, stderr, 0, 'command', '')
32
+ Bolt::Result.for_command(target, stdout, stderr, 0, 'command', '', [])
33
33
  end
34
34
 
35
35
  # Public methods
@@ -35,7 +35,7 @@ module BoltSpec
35
35
  end
36
36
 
37
37
  def result_for(target, stdout: '', stderr: '')
38
- Bolt::Result.for_command(target, stdout, stderr, 0, 'script', '')
38
+ Bolt::Result.for_command(target, stdout, stderr, 0, 'script', '', [])
39
39
  end
40
40
 
41
41
  # Public methods
@@ -48,7 +48,7 @@ module BoltSpec
48
48
  ([segments[0]] + segments[2..-1]).join('/')
49
49
  end
50
50
 
51
- def run_command(targets, command, options = {})
51
+ def run_command(targets, command, options = {}, _position = [])
52
52
  result = nil
53
53
  if (doub = @command_doubles[command] || @command_doubles[:default])
54
54
  result = doub.process(targets, command, options)
@@ -61,7 +61,7 @@ module BoltSpec
61
61
  result
62
62
  end
63
63
 
64
- def run_script(targets, script_path, arguments, options = {})
64
+ def run_script(targets, script_path, arguments, options = {}, _position = [])
65
65
  script = module_file_id(script_path)
66
66
  result = nil
67
67
  if (doub = @script_doubles[script] || @script_doubles[:default])
@@ -76,7 +76,7 @@ module BoltSpec
76
76
  result
77
77
  end
78
78
 
79
- def run_task(targets, task, arguments, options = {})
79
+ def run_task(targets, task, arguments, options = {}, _position = [])
80
80
  result = nil
81
81
  if (doub = @task_doubles[task.name] || @task_doubles[:default])
82
82
  result = doub.process(targets, task.name, arguments, options)
@@ -90,7 +90,7 @@ module BoltSpec
90
90
  result
91
91
  end
92
92
 
93
- def download_file(targets, source, destination, options = {})
93
+ def download_file(targets, source, destination, options = {}, _position = [])
94
94
  result = nil
95
95
  if (doub = @download_doubles[source] || @download_doubles[:default])
96
96
  result = doub.process(targets, source, destination, options)
@@ -103,7 +103,7 @@ module BoltSpec
103
103
  result
104
104
  end
105
105
 
106
- def upload_file(targets, source_path, destination, options = {})
106
+ def upload_file(targets, source_path, destination, options = {}, _position = [])
107
107
  source = module_file_id(source_path)
108
108
  result = nil
109
109
  if (doub = @upload_doubles[source] || @upload_doubles[:default])
@@ -179,7 +179,7 @@ module BoltSpec
179
179
  end
180
180
 
181
181
  def pal
182
- @pal ||= Bolt::PAL.new(config.modulepath,
182
+ @pal ||= Bolt::PAL.new(Bolt::Config::Modulepath.new(config.modulepath),
183
183
  config.hiera_config,
184
184
  config.project.resource_types,
185
185
  config.compile_concurrency)
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.30.0
4
+ version: 2.34.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-09-30 00:00:00.000000000 Z
11
+ date: 2020-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -182,16 +182,22 @@ dependencies:
182
182
  name: puppet
183
183
  requirement: !ruby/object:Gem::Requirement
184
184
  requirements:
185
- - - '='
185
+ - - ">="
186
186
  - !ruby/object:Gem::Version
187
187
  version: 6.18.0
188
+ - - "<"
189
+ - !ruby/object:Gem::Version
190
+ version: '6.20'
188
191
  type: :runtime
189
192
  prerelease: false
190
193
  version_requirements: !ruby/object:Gem::Requirement
191
194
  requirements:
192
- - - '='
195
+ - - ">="
193
196
  - !ruby/object:Gem::Version
194
197
  version: 6.18.0
198
+ - - "<"
199
+ - !ruby/object:Gem::Version
200
+ version: '6.20'
195
201
  - !ruby/object:Gem::Dependency
196
202
  name: puppetfile-resolver
197
203
  requirement: !ruby/object:Gem::Requirement
@@ -432,6 +438,9 @@ files:
432
438
  - bolt-modules/system/lib/puppet/functions/system/env.rb
433
439
  - exe/bolt
434
440
  - guides/inventory.txt
441
+ - guides/logging.txt
442
+ - guides/module.txt
443
+ - guides/modulepath.txt
435
444
  - guides/project.txt
436
445
  - lib/bolt.rb
437
446
  - lib/bolt/analytics.rb
@@ -444,6 +453,7 @@ files:
444
453
  - lib/bolt/catalog/logging.rb
445
454
  - lib/bolt/cli.rb
446
455
  - lib/bolt/config.rb
456
+ - lib/bolt/config/modulepath.rb
447
457
  - lib/bolt/config/options.rb
448
458
  - lib/bolt/config/transport/base.rb
449
459
  - lib/bolt/config/transport/docker.rb
@@ -462,6 +472,15 @@ files:
462
472
  - lib/bolt/logger.rb
463
473
  - lib/bolt/module.rb
464
474
  - lib/bolt/module_installer.rb
475
+ - lib/bolt/module_installer/installer.rb
476
+ - lib/bolt/module_installer/puppetfile.rb
477
+ - lib/bolt/module_installer/puppetfile/forge_module.rb
478
+ - lib/bolt/module_installer/puppetfile/git_module.rb
479
+ - lib/bolt/module_installer/puppetfile/module.rb
480
+ - lib/bolt/module_installer/resolver.rb
481
+ - lib/bolt/module_installer/specs.rb
482
+ - lib/bolt/module_installer/specs/forge_spec.rb
483
+ - lib/bolt/module_installer/specs/git_spec.rb
465
484
  - lib/bolt/node/errors.rb
466
485
  - lib/bolt/node/output.rb
467
486
  - lib/bolt/outputter.rb
@@ -487,6 +506,7 @@ files:
487
506
  - lib/bolt/pal/yaml_plan/step/task.rb
488
507
  - lib/bolt/pal/yaml_plan/step/upload.rb
489
508
  - lib/bolt/pal/yaml_plan/transpiler.rb
509
+ - lib/bolt/plan_creator.rb
490
510
  - lib/bolt/plan_result.rb
491
511
  - lib/bolt/plugin.rb
492
512
  - lib/bolt/plugin/env_var.rb
@@ -503,9 +523,6 @@ files:
503
523
  - lib/bolt/puppetdb.rb
504
524
  - lib/bolt/puppetdb/client.rb
505
525
  - lib/bolt/puppetdb/config.rb
506
- - lib/bolt/puppetfile.rb
507
- - lib/bolt/puppetfile/installer.rb
508
- - lib/bolt/puppetfile/module.rb
509
526
  - lib/bolt/r10k_log_proxy.rb
510
527
  - lib/bolt/rerun.rb
511
528
  - lib/bolt/resource_instance.rb
@@ -542,7 +559,6 @@ files:
542
559
  - lib/bolt_server/base_config.rb
543
560
  - lib/bolt_server/config.rb
544
561
  - lib/bolt_server/file_cache.rb
545
- - lib/bolt_server/pe/pal.rb
546
562
  - lib/bolt_server/schemas/action-check_node_connections.json
547
563
  - lib/bolt_server/schemas/action-run_command.json
548
564
  - lib/bolt_server/schemas/action-run_script.json
@@ -583,7 +599,6 @@ files:
583
599
  - modules/canary/lib/puppet/functions/canary/skip.rb
584
600
  - modules/canary/plans/init.pp
585
601
  - modules/puppetdb_fact/plans/init.pp
586
- - modules/secure_env_vars/plans/init.pp
587
602
  homepage: https://github.com/puppetlabs/bolt
588
603
  licenses:
589
604
  - Apache-2.0
@@ -1,142 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bolt/error'
4
- require 'bolt/puppetfile/module'
5
-
6
- # This class manages the logical contents of a Puppetfile. It includes methods
7
- # for parsing a Puppetfile and its modules, resolving module dependencies,
8
- # and writing a Puppetfile.
9
- #
10
- module Bolt
11
- class Puppetfile
12
- attr_reader :modules
13
-
14
- def initialize(modules = [])
15
- @modules = Set.new
16
- add_modules(modules)
17
- end
18
-
19
- # Loads a Puppetfile and parses its module specifications, returning a
20
- # Bolt::Puppetfile object with the modules set.
21
- #
22
- def self.parse(path, skip_unsupported_modules: false)
23
- require 'puppetfile-resolver'
24
- require 'puppetfile-resolver/puppetfile/parser/r10k_eval'
25
-
26
- begin
27
- parsed = ::PuppetfileResolver::Puppetfile::Parser::R10KEval.parse(File.read(path))
28
- rescue StandardError => e
29
- raise Bolt::Error.new(
30
- "Unable to parse Puppetfile #{path}: #{e.message}",
31
- 'bolt/puppetfile-parsing'
32
- )
33
- end
34
-
35
- unless parsed.valid?
36
- # valid? Just checks if validation_errors is empty, so if we get here we know it's not.
37
- raise Bolt::ValidationError, <<~MSG
38
- Unable to parse Puppetfile #{path}:
39
- #{parsed.validation_errors.join("\n\n")}.
40
- This may not be a Puppetfile managed by Bolt.
41
- MSG
42
- end
43
-
44
- modules = parsed.modules.each_with_object([]) do |mod, acc|
45
- unless mod.instance_of? PuppetfileResolver::Puppetfile::ForgeModule
46
- next if skip_unsupported_modules
47
-
48
- raise Bolt::ValidationError,
49
- "Module '#{mod.title}' is not a Puppet Forge module. Unable to "\
50
- "parse Puppetfile #{path}."
51
- end
52
-
53
- acc << Bolt::Puppetfile::Module.new(mod.owner, mod.name, mod.version)
54
- end
55
-
56
- new(modules)
57
- end
58
-
59
- # Writes a Puppetfile that includes specifications for each of the
60
- # modules.
61
- #
62
- def write(path, moduledir = nil)
63
- File.open(path, 'w') do |file|
64
- file.puts '# This Puppetfile is managed by Bolt. Do not edit.'
65
- file.puts "moduledir '#{moduledir.basename}'" if moduledir
66
- modules.each { |mod| file.puts mod.to_spec }
67
- end
68
- rescue SystemCallError => e
69
- raise Bolt::FileError.new(
70
- "#{e.message}: unable to write Puppetfile.",
71
- path
72
- )
73
- end
74
-
75
- # Resolves module dependencies using the puppetfile-resolver library. The
76
- # resolver will return a document model including all module dependencies
77
- # and the latest version that can be installed for each. The document model
78
- # is parsed and turned into a Set of Bolt::Puppetfile::Module objects.
79
- #
80
- def resolve
81
- require 'puppetfile-resolver'
82
-
83
- # Build the document model from the modules.
84
- model = PuppetfileResolver::Puppetfile::Document.new('')
85
-
86
- @modules.each do |mod|
87
- model.add_module(
88
- PuppetfileResolver::Puppetfile::ForgeModule.new(mod.title).tap do |tap|
89
- tap.version = mod.version || :latest
90
- end
91
- )
92
- end
93
-
94
- # Make sure the Puppetfile model is valid.
95
- unless model.valid?
96
- raise Bolt::ValidationError,
97
- "Unable to resolve dependencies for modules: #{@modules.map(&:title).join(', ')}"
98
- end
99
-
100
- # Create the resolver using the Puppetfile model. nil disables Puppet
101
- # version restrictions.
102
- resolver = PuppetfileResolver::Resolver.new(model, nil)
103
-
104
- # Configure and resolve the dependency graph, catching any errors
105
- # raised by puppetfile-resolver and re-raising them as Bolt errors.
106
- begin
107
- result = resolver.resolve(
108
- cache: nil,
109
- ui: nil,
110
- module_paths: [],
111
- allow_missing_modules: false
112
- )
113
- rescue StandardError => e
114
- raise Bolt::Error.new(e.message, 'bolt/puppetfile-resolver-error')
115
- end
116
-
117
- # Turn specifications into module objects. This will skip over anything that is not
118
- # a module specification (i.e. a Puppet version specification).
119
- @modules = result.specifications.each_with_object(Set.new) do |(_name, spec), acc|
120
- next unless spec.instance_of? PuppetfileResolver::Models::ModuleSpecification
121
- acc << Bolt::Puppetfile::Module.new(spec.owner, spec.name, spec.version.to_s)
122
- end
123
- end
124
-
125
- # Adds to the set of modules.
126
- #
127
- def add_modules(modules)
128
- Array(modules).each do |mod|
129
- case mod
130
- when Bolt::Puppetfile::Module
131
- @modules << mod
132
- when Hash
133
- @modules << Bolt::Puppetfile::Module.from_hash(mod)
134
- else
135
- raise Bolt::ValidationError, "Module must be a Bolt::Puppetfile::Module or Hash."
136
- end
137
- end
138
-
139
- @modules
140
- end
141
- end
142
- end