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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c8ca2906e21e39d1da306c9a02ddb970f64955133fcab01574e4138c1e6e154e
4
- data.tar.gz: 4c875d82bf7ff9d4117440eca9e1276c1b9c2fa842fda4669227ebb0ec56f170
3
+ metadata.gz: 5d33993b3430c33f173a3a2d07220d175d4545c1992e9bed3bd75a7be7bae9be
4
+ data.tar.gz: 68fe674fd2d8ec196149938668d68b3e4c2becd3a97639c49ee85a637bb2e5a8
5
5
  SHA512:
6
- metadata.gz: bd8e4327ddfeb4d88fdd1f94ab4233bcf1f0d8ce1390769587197dec11bfa024a16889c5693ec4a4333565cd7553a8c13487b6674ba5427a63f9408961e6a3a7
7
- data.tar.gz: f2bcbe28ba4c26bc36d3c52ab89a8bafa6a132c930c310a92e48b841d0274389022f2480d3ed397bcc4bcf1f77fb272da82697362d33feccfe05ab0ca203cf6c
6
+ metadata.gz: 89e8fd86df6d1427fa61df4e54b0a1973ec607bcd46744a5fa4f0316f217ae02fa0b72428527a6d3ef15303f9fae371c62954d5b53a600d4d71589a3b2108656
7
+ data.tar.gz: e40ee91802e4dcb6a5d542f131800b28f4916ad2245837b34f26bcef128d99485c3d96ab02e498033476fe01fe6e5740be74b985370fd390ded7b8b0683b0e04
data/Puppetfile CHANGED
@@ -6,36 +6,37 @@ moduledir File.join(File.dirname(__FILE__), 'modules')
6
6
 
7
7
  # Core modules used by 'apply'
8
8
  mod 'puppetlabs-service', '1.3.0'
9
- mod 'puppetlabs-puppet_agent', '4.1.1'
10
- mod 'puppetlabs-facts', '1.0.0'
9
+ mod 'puppetlabs-puppet_agent', '4.2.0'
10
+ mod 'puppetlabs-facts', '1.2.0'
11
11
 
12
12
  # Core types and providers for Puppet 6
13
- mod 'puppetlabs-augeas_core', '1.0.5'
13
+ mod 'puppetlabs-augeas_core', '1.1.1'
14
14
  mod 'puppetlabs-host_core', '1.0.3'
15
- mod 'puppetlabs-scheduled_task', '2.0.1'
16
- mod 'puppetlabs-sshkeys_core', '1.0.3'
17
- mod 'puppetlabs-zfs_core', '1.0.4'
18
- mod 'puppetlabs-cron_core', '1.0.3'
15
+ mod 'puppetlabs-scheduled_task', '2.2.1'
16
+ mod 'puppetlabs-sshkeys_core', '2.2.0'
17
+ mod 'puppetlabs-zfs_core', '1.2.0'
18
+ mod 'puppetlabs-cron_core', '1.0.5'
19
19
  mod 'puppetlabs-mount_core', '1.0.4'
20
20
  mod 'puppetlabs-selinux_core', '1.0.4'
21
- mod 'puppetlabs-yumrepo_core', '1.0.6'
21
+ mod 'puppetlabs-yumrepo_core', '1.0.7'
22
22
  mod 'puppetlabs-zone_core', '1.0.3'
23
23
 
24
24
  # Useful additional modules
25
- mod 'puppetlabs-package', '1.1.0'
25
+ mod 'puppetlabs-package', '1.3.0'
26
26
  mod 'puppetlabs-puppet_conf', '0.6.0'
27
27
  mod 'puppetlabs-python_task_helper', '0.4.3'
28
28
  mod 'puppetlabs-reboot', '3.0.0'
29
29
  mod 'puppetlabs-ruby_task_helper', '0.5.1'
30
30
  mod 'puppetlabs-ruby_plugin_helper', '0.1.0'
31
- mod 'puppetlabs-stdlib', '6.3.0'
31
+ mod 'puppetlabs-stdlib', '6.5.0'
32
32
 
33
33
  # Plugin modules
34
34
  mod 'puppetlabs-aws_inventory', '0.5.2'
35
35
  mod 'puppetlabs-azure_inventory', '0.4.1'
36
36
  mod 'puppetlabs-gcloud_inventory', '0.1.3'
37
- mod 'puppetlabs-http_request', '0.1.0'
37
+ mod 'puppetlabs-http_request', '0.2.0'
38
38
  mod 'puppetlabs-pkcs7', '0.1.1'
39
+ mod 'puppetlabs-secure_env_vars', '0.1.0'
39
40
  mod 'puppetlabs-terraform', '0.5.0'
40
41
  mod 'puppetlabs-vault', '0.3.0'
41
42
  mod 'puppetlabs-yaml', '0.2.0'
@@ -44,4 +45,3 @@ mod 'puppetlabs-yaml', '0.2.0'
44
45
  mod 'canary', local: true
45
46
  mod 'aggregate', local: true
46
47
  mod 'puppetdb_fact', local: true
47
- mod 'secure_env_vars', local: true
@@ -112,7 +112,7 @@ Puppet::Functions.create_function(:download_file, Puppet::Functions::InternalFun
112
112
  call_function('debug', "Simulating file download of '#{source}' - no targets given - no action taken")
113
113
  r = Bolt::ResultSet.new([])
114
114
  else
115
- r = executor.download_file(targets, source, destination, options)
115
+ r = executor.download_file(targets, source, destination, options, Puppet::Pops::PuppetStack.top_of_stack)
116
116
  end
117
117
 
118
118
  if !r.ok && !options[:catch_errors]
@@ -3,6 +3,12 @@
3
3
  require 'bolt/error'
4
4
 
5
5
  # Returns the facts hash for a target.
6
+ #
7
+ # Using the `facts` function does not automatically collect facts for a target,
8
+ # and will only return facts that are currently set in the inventory. To collect
9
+ # facts from a target and set them in the inventory, run the
10
+ # [facts](writing_plans.md#collect-facts-from-targets) plan or
11
+ # [puppetdb_fact](writing_plans.md#collect-facts-from-puppetdb) plan.
6
12
  Puppet::Functions.create_function(:facts) do
7
13
  # @param target A target.
8
14
  # @return The target's facts.
@@ -2,12 +2,12 @@
2
2
 
3
3
  require 'bolt/error'
4
4
 
5
- # Makes a query to {https://puppet.com/docs/puppetdb/latest/index.html puppetdb}
5
+ # Makes a query to [puppetdb](https://puppet.com/docs/puppetdb/latest/index.html)
6
6
  # using Bolt's PuppetDB client.
7
7
  Puppet::Functions.create_function(:puppetdb_query) do
8
8
  # rubocop:disable Layout/LineLength
9
9
  # @param query A PQL query.
10
- # {https://puppet.com/docs/puppetdb/latest/api/query/tutorial-pql.html Learn more about Puppet's query language, PQL}
10
+ # Learn more about [Puppet's query language](https://puppet.com/docs/puppetdb/latest/api/query/tutorial-pql.html), PQL.
11
11
  # @return Results of the PuppetDB query.
12
12
  # @example Request certnames for all nodes
13
13
  # puppetdb_query('nodes[certname] {}')
@@ -69,7 +69,7 @@ Puppet::Functions.create_function(:run_command) do
69
69
  call_function('debug', "Simulating run_command('#{command}') - no targets given - no action taken")
70
70
  r = Bolt::ResultSet.new([])
71
71
  else
72
- r = executor.run_command(targets, command, options)
72
+ r = executor.run_command(targets, command, options, Puppet::Pops::PuppetStack.top_of_stack)
73
73
  end
74
74
 
75
75
  if !r.ok && !options[:catch_errors]
@@ -87,7 +87,7 @@ Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFuncti
87
87
  r = if targets.empty?
88
88
  Bolt::ResultSet.new([])
89
89
  else
90
- executor.run_script(targets, found, arguments, options)
90
+ executor.run_script(targets, found, arguments, options, Puppet::Pops::PuppetStack.top_of_stack)
91
91
  end
92
92
 
93
93
  if !r.ok && !options[:catch_errors]
@@ -133,7 +133,7 @@ Puppet::Functions.create_function(:run_task) do
133
133
  if targets.empty?
134
134
  Bolt::ResultSet.new([])
135
135
  else
136
- result = executor.run_task(targets, task, params, options)
136
+ result = executor.run_task(targets, task, params, options, Puppet::Pops::PuppetStack.top_of_stack)
137
137
  if !result.ok && !options[:catch_errors]
138
138
  raise Bolt::RunFailure.new(result, 'run_task', task_name)
139
139
  end
@@ -180,7 +180,7 @@ Puppet::Functions.create_function(:run_task_with) do
180
180
  else
181
181
  # Combine the results from the task run with any failing results that were
182
182
  # generated earlier when creating the target mapping
183
- task_result = executor.run_task_with(target_mapping, task, options)
183
+ task_result = executor.run_task_with(target_mapping, task, options, Puppet::Pops::PuppetStack.top_of_stack)
184
184
  result = Bolt::ResultSet.new(task_result.results + error_set)
185
185
 
186
186
  if !result.ok && !options[:catch_errors]
@@ -83,7 +83,7 @@ Puppet::Functions.create_function(:upload_file, Puppet::Functions::InternalFunct
83
83
  call_function('debug', "Simulating file upload of '#{found}' - no targets given - no action taken")
84
84
  r = Bolt::ResultSet.new([])
85
85
  else
86
- r = executor.upload_file(targets, found, destination, options)
86
+ r = executor.upload_file(targets, found, destination, options, Puppet::Pops::PuppetStack.top_of_stack)
87
87
  end
88
88
 
89
89
  if !r.ok && !options[:catch_errors]
@@ -6,15 +6,15 @@ require 'tempfile'
6
6
  #
7
7
  # > **Note:** Not available in apply block
8
8
  Puppet::Functions.create_function(:write_file) do
9
- # @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
10
9
  # @param content File content to write.
11
10
  # @param destination An absolute path on the target(s).
11
+ # @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
12
12
  # @option options [Boolean] _catch_errors Whether to catch raised errors.
13
13
  # @option options [String] _run_as User to run as using privilege escalation.
14
14
  # @return A list of results, one entry per target.
15
15
  # @example Write a file to a target
16
16
  # $content = 'Hello, world!'
17
- # write_file($targets, $content, '/Users/me/hello.txt')
17
+ # write_file($content, '/Users/me/hello.txt', $targets)
18
18
  dispatch :write_file do
19
19
  required_param 'String', :content
20
20
  required_param 'String[1]', :destination
@@ -26,8 +26,51 @@ Puppet::Functions.create_function(:'out::message') do
26
26
  # Send Analytics Report
27
27
  executor.report_function_call(self.class.name)
28
28
 
29
- executor.publish_event(type: :message, message: message)
29
+ executor.publish_event(type: :message, message: stringify(message))
30
30
 
31
31
  nil
32
32
  end
33
+
34
+ def stringify(message)
35
+ formatted = format_message(message)
36
+ if formatted.is_a?(Hash) || formatted.is_a?(Array)
37
+ ::JSON.pretty_generate(formatted)
38
+ else
39
+ formatted
40
+ end
41
+ end
42
+
43
+ def format_message(message)
44
+ case message
45
+ when Array
46
+ message.map { |item| format_message(item) }
47
+ when Bolt::ApplyResult
48
+ format_apply_result(message)
49
+ when Bolt::Result, Bolt::ResultSet
50
+ # This is equivalent to to_s, but formattable
51
+ message.to_data
52
+ when Bolt::RunFailure
53
+ formatted_resultset = message.result_set.to_data
54
+ message.to_h.merge('result_set' => formatted_resultset)
55
+ when Hash
56
+ message.each_with_object({}) do |(k, v), h|
57
+ h[format_message(k)] = format_message(v)
58
+ end
59
+ when Integer, Float, NilClass
60
+ message
61
+ else
62
+ message.to_s
63
+ end
64
+ end
65
+
66
+ def format_apply_result(result)
67
+ logs = result.resource_logs&.map do |log|
68
+ # Omit low-level info/debug messages
69
+ next if %w[info debug].include?(log['level'])
70
+ indent(2, format_log(log))
71
+ end
72
+ hash = result.to_data
73
+ hash['logs'] = logs unless logs.empty?
74
+ hash
75
+ end
33
76
  end
@@ -9,11 +9,14 @@ Puppet::Functions.create_function(:prompt) do
9
9
  # @param prompt The prompt to display.
10
10
  # @param options A hash of additional options.
11
11
  # @option options [Boolean] sensitive Disable echo back and mark the response as sensitive.
12
+ # The returned value will be wrapped by the `Sensitive` data type. To access the raw
13
+ # value, use the `unwrap` function (i.e. `$sensitive_value.unwrap`).
12
14
  # @return The response to the prompt.
13
15
  # @example Prompt the user if plan execution should continue
14
16
  # $response = prompt('Continue executing plan? [Y\N]')
15
17
  # @example Prompt the user for sensitive information
16
18
  # $password = prompt('Enter your password', 'sensitive' => true)
19
+ # out::message("Password is: ${password.unwrap}")
17
20
  dispatch :prompt do
18
21
  param 'String', :prompt
19
22
  optional_param 'Hash[String[1], Any]', :options
@@ -0,0 +1,18 @@
1
+ TOPIC
2
+ logging
3
+
4
+ DESCRIPTION
5
+ Bolt prints messages both to the console and to log files. Messages can
6
+ either come from Bolt's 'outputter', which logs user-facing messages like
7
+ progress and results, or from the 'logger', which logs warnings, errors, and
8
+ log-structured output to log files. Both of these message streams are
9
+ configurable.
10
+
11
+ By default, Bolt logs to the console at 'warn' level and writes a log file to
12
+ '<project>/bolt-debug.log' at 'debug' level. Unless you are running a plan,
13
+ Bolt runs in verbose mode by default.
14
+
15
+ To learn more about projects, see the 'project' guide.
16
+
17
+ DOCUMENTATION
18
+ https://pup.pt/bolt-logging
@@ -0,0 +1,19 @@
1
+ TOPIC
2
+ module
3
+
4
+ DESCRIPTION
5
+ Modules are shareable, reusable packages of Puppet content. They can include
6
+ tasks, plans, functions, and other types of content that you can use in your
7
+ project. You can download and install modules to your project from the
8
+ Puppet Forge or write your own modules. Bolt also ships with several helpful
9
+ modules pre-installed that are available to all of your projects.
10
+
11
+ Bolt makes it easy to manage the modules that your project depends on. You
12
+ can use Bolt commands to install a project's modules, add new modules to a
13
+ project, and view the modules that are available to the project.
14
+
15
+ To learn more about managing modules in a project, see the documentation.
16
+ To learn how modules are loaded by Bolt, see the 'modulepath' guide.
17
+
18
+ DOCUMENTATION
19
+ https://pup.pt/bolt-modules
@@ -0,0 +1,25 @@
1
+ TOPIC
2
+ modulepath
3
+
4
+ DESCRIPTION
5
+ The modulepath is an ordered list of directories that Bolt loads modules
6
+ from. When Bolt runs a command, it automatically loads modules from the
7
+ modulepath.
8
+
9
+ While Bolt has a default modulepath, you can also configure your own
10
+ modulepath, which can include directories within the project or directories
11
+ elsewhere on your system. Regardless of whether your project uses a default
12
+ or configured modulepath, Bolt automatically adds directories to the
13
+ modulepath. This includes modules containing core Bolt content, which is
14
+ added to the beginning of the modulepath, and bundled content, which is
15
+ added to the end of the modulepath.
16
+
17
+ Modules loaded from a directory listed earlier in the modulepath take
18
+ precedence over modules with the same name loaded from a directory later in
19
+ the modulepath. Bolt will not warn or error when two modules share a name
20
+ and instead will ignore modules with a lower precedence.
21
+
22
+ To learn more about modules, see the 'module' guide.
23
+
24
+ DOCUMENTATION
25
+ https://pup.pt/bolt-project-reference#modulepath
@@ -88,7 +88,7 @@ module Bolt
88
88
  { flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
89
89
  banner: PLAN_CONVERT_HELP }
90
90
  when 'new'
91
- { flags: OPTIONS[:global] + %w[configfile project],
91
+ { flags: OPTIONS[:global] + %w[configfile project pp],
92
92
  banner: PLAN_NEW_HELP }
93
93
  when 'run'
94
94
  { flags: ACTION_OPTS + %w[params compile-concurrency tmpdir hiera-config],
@@ -945,6 +945,11 @@ module Bolt
945
945
  @options[:resolve] = resolve
946
946
  end
947
947
 
948
+ separator "\nPLAN OPTIONS"
949
+ define('--pp', 'Create a new Puppet language plan.') do |pp|
950
+ @options[:puppet] = pp
951
+ end
952
+
948
953
  separator "\nDISPLAY OPTIONS"
949
954
  define('--filter FILTER', 'Filter tasks and plans by a matching substring') do |filter|
950
955
  unless /^[a-z0-9_:]+$/.match(filter)
@@ -15,17 +15,18 @@ require 'bolt/config'
15
15
  require 'bolt/error'
16
16
  require 'bolt/executor'
17
17
  require 'bolt/inventory'
18
- require 'bolt/rerun'
19
18
  require 'bolt/logger'
19
+ require 'bolt/module_installer'
20
20
  require 'bolt/outputter'
21
- require 'bolt/puppetdb'
21
+ require 'bolt/pal'
22
+ require 'bolt/plan_creator'
22
23
  require 'bolt/plugin'
23
24
  require 'bolt/project_migrator'
24
- require 'bolt/pal'
25
+ require 'bolt/puppetdb'
26
+ require 'bolt/rerun'
27
+ require 'bolt/secret'
25
28
  require 'bolt/target'
26
29
  require 'bolt/version'
27
- require 'bolt/secret'
28
- require 'bolt/module_installer'
29
30
 
30
31
  module Bolt
31
32
  class CLIExit < StandardError; end
@@ -196,10 +197,6 @@ module Bolt
196
197
 
197
198
  warn_inventory_overrides_cli(options)
198
199
 
199
- # Assert whether the puppetfile/module commands are available depending
200
- # on whether 'modules' is configured.
201
- assert_puppetfile_or_module_command(config.project.modules)
202
-
203
200
  options
204
201
  rescue Bolt::Error => e
205
202
  outputter.fatal_error(e)
@@ -228,9 +225,9 @@ module Bolt
228
225
 
229
226
  def validate(options)
230
227
  unless COMMANDS.include?(options[:subcommand])
228
+ command = Bolt::Util.powershell? ? 'Get-Command -Module PuppetBolt' : 'bolt help'
231
229
  raise Bolt::CLIError,
232
- "Expected subcommand '#{options[:subcommand]}' to be one of " \
233
- "#{COMMANDS.keys.join(', ')}"
230
+ "'#{options[:subcommand]}' is not a Bolt command. See '#{command}'."
234
231
  end
235
232
 
236
233
  actions = COMMANDS[options[:subcommand]]
@@ -285,8 +282,9 @@ module Bolt
285
282
  end
286
283
 
287
284
  if options[:subcommand] == 'module' && options[:action] == 'install' && options[:object]
285
+ command = Bolt::Util.powershell? ? 'Add-BoltModule -Module' : 'bolt module add'
288
286
  raise Bolt::CLIError, "Invalid argument '#{options[:object]}'. To add a new module to "\
289
- "the project, run 'bolt module add #{options[:object]}'."
287
+ "the project, run '#{command} #{options[:object]}'."
290
288
  end
291
289
 
292
290
  if options[:subcommand] != 'file' && options[:subcommand] != 'script' &&
@@ -465,7 +463,16 @@ module Bolt
465
463
  when 'plan'
466
464
  case options[:action]
467
465
  when 'new'
468
- code = new_plan(options[:object])
466
+ command = Bolt::Util.powershell? ? 'New-BoltPlan' : 'bolt plan new'
467
+ @logger.warn("Command '#{command}' is experimental and subject to changes.")
468
+ plan_name = options[:object]
469
+
470
+ # If this passes validation, it will return the path to the plan to create
471
+ Bolt::PlanCreator.validate_input(config.project, plan_name)
472
+ code = Bolt::PlanCreator.create_plan(config.project.plans_path,
473
+ plan_name,
474
+ outputter,
475
+ options[:puppet])
469
476
  when 'run'
470
477
  code = run_plan(options[:object], options[:task_options], options[:target_args], options)
471
478
  end
@@ -587,8 +594,24 @@ module Bolt
587
594
  end
588
595
 
589
596
  def list_targets
597
+ inventoryfile = config.inventoryfile || config.default_inventoryfile
598
+
599
+ # Retrieve the known group and target names. This needs to be done before
600
+ # updating targets, as that will add adhoc targets to the inventory.
601
+ known_names = inventory.target_names
602
+
590
603
  update_targets(options)
591
- outputter.print_targets(options[:targets])
604
+
605
+ inventory_targets, adhoc_targets = options[:targets].partition do |target|
606
+ known_names.include?(target.name)
607
+ end
608
+
609
+ target_list = {
610
+ inventory: inventory_targets,
611
+ adhoc: adhoc_targets
612
+ }
613
+
614
+ outputter.print_targets(target_list, inventoryfile)
592
615
  end
593
616
 
594
617
  def show_targets
@@ -601,118 +624,6 @@ module Bolt
601
624
  outputter.print_groups(groups)
602
625
  end
603
626
 
604
- def new_plan(plan_name)
605
- @logger.warn("Command 'bolt plan new' is experimental and subject to changes.")
606
-
607
- if config.project.name.nil?
608
- raise Bolt::Error.new(
609
- "Project directory '#{config.project.path}' is not a named project. Unable to create "\
610
- "a project-level plan. To name a project, set the 'name' key in the 'bolt-project.yaml' "\
611
- "configuration file.",
612
- "bolt/unnamed-project-error"
613
- )
614
- end
615
-
616
- if plan_name !~ Bolt::Module::CONTENT_NAME_REGEX
617
- message = <<~MESSAGE.chomp
618
- Invalid plan name '#{plan_name}'. Plan names are composed of one or more name segments
619
- separated by double colons '::'.
620
-
621
- Each name segment must begin with a lowercase letter, and may only include lowercase
622
- letters, digits, and underscores.
623
-
624
- Examples of valid plan names:
625
- - #{config.project.name}
626
- - #{config.project.name}::my_plan
627
- MESSAGE
628
-
629
- raise Bolt::ValidationError, message
630
- end
631
-
632
- prefix, *name_segments, basename = plan_name.split('::')
633
-
634
- # If the plan name is just the project name, then create an 'init' plan.
635
- # Otherwise, use the last name segment for the plan's filename.
636
- basename ||= 'init'
637
-
638
- unless prefix == config.project.name
639
- message = "First segment of plan name '#{plan_name}' must match project name '#{config.project.name}'. "\
640
- "Did you mean '#{config.project.name}::#{plan_name}'?"
641
-
642
- raise Bolt::ValidationError, message
643
- end
644
-
645
- dir_path = config.project.plans_path.join(*name_segments)
646
-
647
- %w[pp yaml].each do |ext|
648
- next unless (path = config.project.plans_path + "#{basename}.#{ext}").exist?
649
- raise Bolt::Error.new(
650
- "A plan with the name '#{plan_name}' already exists at '#{path}', nothing to do.",
651
- 'bolt/existing-plan-error'
652
- )
653
- end
654
-
655
- begin
656
- FileUtils.mkdir_p(dir_path)
657
- rescue Errno::EEXIST => e
658
- raise Bolt::Error.new(
659
- "#{e.message}; unable to create plan directory '#{dir_path}'",
660
- 'bolt/existing-file-error'
661
- )
662
- end
663
-
664
- plan_path = dir_path + "#{basename}.yaml"
665
-
666
- plan_template = <<~PLAN
667
- # This is the structure of a simple plan. To learn more about writing
668
- # YAML plans, see the documentation: http://pup.pt/bolt-yaml-plans
669
-
670
- # The description sets the description of the plan that will appear
671
- # in 'bolt plan show' output.
672
- description: A plan created with bolt plan new
673
-
674
- # The parameters key defines the parameters that can be passed to
675
- # the plan.
676
- parameters:
677
- targets:
678
- type: TargetSpec
679
- description: A list of targets to run actions on
680
- default: localhost
681
-
682
- # The steps key defines the actions the plan will take in order.
683
- steps:
684
- - message: Hello from #{plan_name}
685
- - name: command_step
686
- command: whoami
687
- targets: $targets
688
-
689
- # The return key sets the return value of the plan.
690
- return: $command_step
691
- PLAN
692
-
693
- begin
694
- File.write(plan_path, plan_template)
695
- rescue Errno::EACCES => e
696
- raise Bolt::FileError.new(
697
- "#{e.message}; unable to create plan",
698
- plan_path
699
- )
700
- end
701
-
702
- output = <<~OUTPUT
703
- Created plan '#{plan_name}' at '#{plan_path}'
704
-
705
- Show this plan with:
706
- bolt plan show #{plan_name}
707
- Run this plan with:
708
- bolt plan run #{plan_name}
709
- OUTPUT
710
-
711
- outputter.print_message(output)
712
-
713
- 0
714
- end
715
-
716
627
  def run_plan(plan_name, plan_arguments, nodes, options)
717
628
  unless nodes.empty?
718
629
  if plan_arguments['nodes'] || plan_arguments['targets']
@@ -804,10 +715,12 @@ module Bolt
804
715
  end
805
716
 
806
717
  def list_modules
718
+ assert_puppetfile_or_module_command(config.project.modules)
807
719
  outputter.print_module_list(pal.list_modules)
808
720
  end
809
721
 
810
722
  def generate_types
723
+ assert_puppetfile_or_module_command(config.project.modules)
811
724
  # generate_types will surface a nice error with helpful message if it fails
812
725
  pal.generate_types
813
726
  0
@@ -828,8 +741,9 @@ module Bolt
828
741
  "project name must begin with a lowercase letter and can include lowercase "\
829
742
  "letters, numbers, and underscores."
830
743
  else
744
+ command = Bolt::Util.powershell? ? 'New-BoltProject -Name <NAME>' : 'bolt project init <NAME>'
831
745
  raise Bolt::ValidationError, "The current directory name '#{name}' is an invalid "\
832
- "project name. Please specify a name using 'bolt project init <name>'."
746
+ "project name. Please specify a name using '#{command}'."
833
747
  end
834
748
  end
835
749
 
@@ -886,6 +800,7 @@ module Bolt
886
800
  #
887
801
  def install_project_modules(project, force, resolve)
888
802
  assert_project_file(project)
803
+ assert_puppetfile_or_module_command(project.modules)
889
804
 
890
805
  unless project.modules
891
806
  outputter.print_message "Project configuration file #{project.project_file} does not "\
@@ -907,6 +822,7 @@ module Bolt
907
822
  #
908
823
  def add_project_module(name, project)
909
824
  assert_project_file(project)
825
+ assert_puppetfile_or_module_command(project.modules)
910
826
 
911
827
  modules = project.modules || []
912
828
  installer = Bolt::ModuleInstaller.new(outputter, pal)
@@ -924,11 +840,13 @@ module Bolt
924
840
  def assert_project_file(project)
925
841
  unless project.project_file?
926
842
  msg = if project.config_file.exist?
843
+ command = Bolt::Util.powershell? ? 'Update-BoltProject' : 'bolt project migrate'
927
844
  "Detected Bolt configuration file #{project.config_file}, unable to install "\
928
- "modules. To update to a project configuration file, run 'bolt project migrate'."
845
+ "modules. To update to a project configuration file, run '#{command}'."
929
846
  else
847
+ command = Bolt::Util.powershell? ? 'New-BoltProject' : 'bolt project init'
930
848
  "Could not find project configuration file #{project.project_file}, unable "\
931
- "to install modules. To create a Bolt project, run 'bolt project init'."
849
+ "to install modules. To create a Bolt project, run '#{command}'."
932
850
  end
933
851
 
934
852
  raise Bolt::Error.new(msg, 'bolt/missing-project-config-error')
@@ -937,9 +855,12 @@ module Bolt
937
855
 
938
856
  # Loads a Puppetfile and installs its modules.
939
857
  #
940
- def install_puppetfile(config, puppetfile, moduledir)
858
+ def install_puppetfile(puppetfile_config, puppetfile, moduledir)
859
+ assert_puppetfile_or_module_command(config.project.modules)
860
+
861
+ outputter.print_message("Installing modules from Puppetfile")
941
862
  installer = Bolt::ModuleInstaller.new(outputter, pal)
942
- ok = installer.install_puppetfile(puppetfile, moduledir, config)
863
+ ok = installer.install_puppetfile(puppetfile, moduledir, puppetfile_config)
943
864
  ok ? 0 : 1
944
865
  end
945
866
 
@@ -947,22 +868,41 @@ module Bolt
947
868
  # modules being configured.
948
869
  #
949
870
  def assert_puppetfile_or_module_command(modules)
871
+ if Bolt::Util.powershell?
872
+ case options[:action]
873
+ when 'generate-types'
874
+ old_command = 'Register-BoltPuppetfileTypes'
875
+ new_command = 'Register-BoltModuleTypes'
876
+ when 'install'
877
+ old_command = 'Install-BoltPuppetfile'
878
+ new_command = 'Install-BoltModule'
879
+ when 'show', 'show-modules'
880
+ old_command = 'Get-BoltPuppetfileModules'
881
+ new_command = 'Get-BoltModule'
882
+ end
883
+ else
884
+ old_command = "bolt puppetfile #{options[:action]}"
885
+ new_command = if options[:action] == 'show-modules'
886
+ 'bolt module show'
887
+ else
888
+ "bolt module #{options[:action]}"
889
+ end
890
+ end
891
+
950
892
  if modules && options[:subcommand] == 'puppetfile'
951
893
  raise Bolt::CLIError,
952
- "Unable to use command 'bolt puppetfile #{options[:action]}' when "\
953
- "'modules' is configured in bolt-project.yaml. Use the 'module' command "\
954
- "instead. For a list of available actions for the 'module' command, run "\
955
- "'bolt module --help'."
894
+ "Unable to use command '#{old_command}' when 'modules' is configured in "\
895
+ "bolt-project.yaml. Use '#{new_command}' instead."
956
896
  elsif modules.nil? && options[:subcommand] == 'module'
957
- raise Bolt::CLIError,
958
- "Unable to use command 'bolt module #{options[:action]}'. To use "\
959
- "this command, update your project configuration to manage module "\
960
- "dependencies."
897
+ msg = "Unable to use command '#{new_command}' when 'modules' is not configured in "\
898
+ "bolt-project.yaml. "
899
+ msg += "Use '#{old_command}' instead." if options[:action] != 'add'
900
+ raise Bolt::CLIError, msg
961
901
  end
962
902
  end
963
903
 
964
904
  def pal
965
- @pal ||= Bolt::PAL.new(config.modulepath,
905
+ @pal ||= Bolt::PAL.new(Bolt::Config::Modulepath.new(config.modulepath),
966
906
  config.hiera_config,
967
907
  config.project.resource_types,
968
908
  config.compile_concurrency,
@@ -1061,7 +1001,7 @@ module Bolt
1061
1001
  'Task' => [],
1062
1002
  'Plugin' => Bolt::Plugin::BUILTIN_PLUGINS }
1063
1003
  if %w[plan task].include?(options[:subcommand]) && options[:action] == 'run'
1064
- default_content = Bolt::PAL.new([], nil, nil)
1004
+ default_content = Bolt::PAL.new(Bolt::Config::Modulepath.new([]), nil, nil)
1065
1005
  content['Plan'] = default_content.list_plans.each_with_object([]) do |iter, col|
1066
1006
  col << iter&.first
1067
1007
  end
@@ -1076,7 +1016,7 @@ module Bolt
1076
1016
  # Gem installs include the aggregate, canary, and puppetdb_fact modules, while
1077
1017
  # package installs include modules listed in the Bolt repo Puppetfile
1078
1018
  def incomplete_install?
1079
- (Dir.children(Bolt::PAL::MODULES_PATH) - %w[aggregate canary puppetdb_fact secure_env_vars]).empty?
1019
+ (Dir.children(Bolt::Config::Modulepath::MODULES_PATH) - %w[aggregate canary puppetdb_fact secure_env_vars]).empty?
1080
1020
  end
1081
1021
 
1082
1022
  # Mimicks the output from Outputter::Human#fatal_error. This should be used to print