bolt 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
 - data/Puppetfile +8 -8
 - data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -1
 - data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +2 -2
 - data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +27 -5
 - data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
 - data/bolt-modules/file/lib/puppet/functions/file/read.rb +3 -2
 - data/lib/bolt/apply_result.rb +1 -1
 - data/lib/bolt/bolt_option_parser.rb +6 -3
 - data/lib/bolt/cli.rb +37 -12
 - data/lib/bolt/config.rb +4 -0
 - data/lib/bolt/config/options.rb +21 -3
 - data/lib/bolt/config/transport/lxd.rb +21 -0
 - data/lib/bolt/config/transport/options.rb +1 -1
 - data/lib/bolt/executor.rb +10 -3
 - data/lib/bolt/logger.rb +8 -0
 - data/lib/bolt/module_installer.rb +2 -2
 - data/lib/bolt/module_installer/puppetfile.rb +2 -2
 - data/lib/bolt/module_installer/specs/forge_spec.rb +2 -2
 - data/lib/bolt/module_installer/specs/git_spec.rb +2 -2
 - data/lib/bolt/outputter/human.rb +47 -12
 - data/lib/bolt/pal.rb +2 -2
 - data/lib/bolt/pal/yaml_plan.rb +1 -2
 - data/lib/bolt/pal/yaml_plan/evaluator.rb +5 -141
 - data/lib/bolt/pal/yaml_plan/step.rb +91 -31
 - data/lib/bolt/pal/yaml_plan/step/command.rb +16 -16
 - data/lib/bolt/pal/yaml_plan/step/download.rb +15 -16
 - data/lib/bolt/pal/yaml_plan/step/eval.rb +11 -11
 - data/lib/bolt/pal/yaml_plan/step/message.rb +13 -4
 - data/lib/bolt/pal/yaml_plan/step/plan.rb +19 -15
 - data/lib/bolt/pal/yaml_plan/step/resources.rb +82 -21
 - data/lib/bolt/pal/yaml_plan/step/script.rb +32 -17
 - data/lib/bolt/pal/yaml_plan/step/task.rb +19 -16
 - data/lib/bolt/pal/yaml_plan/step/upload.rb +16 -17
 - data/lib/bolt/pal/yaml_plan/transpiler.rb +1 -1
 - data/lib/bolt/plan_creator.rb +1 -1
 - data/lib/bolt/project_manager.rb +1 -1
 - data/lib/bolt/project_manager/module_migrator.rb +1 -1
 - data/lib/bolt/shell.rb +16 -0
 - data/lib/bolt/shell/bash.rb +48 -21
 - data/lib/bolt/shell/bash/tmpdir.rb +2 -2
 - data/lib/bolt/shell/powershell.rb +24 -5
 - data/lib/bolt/task.rb +1 -1
 - data/lib/bolt/transport/lxd.rb +26 -0
 - data/lib/bolt/transport/lxd/connection.rb +99 -0
 - data/lib/bolt/transport/ssh/connection.rb +1 -1
 - data/lib/bolt/transport/winrm/connection.rb +1 -1
 - data/lib/bolt/version.rb +1 -1
 - data/lib/bolt_server/transport_app.rb +13 -1
 - data/lib/bolt_spec/plans/action_stubs.rb +1 -1
 - data/lib/bolt_spec/plans/mock_executor.rb +4 -0
 - metadata +5 -2
 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'bolt/error'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'bolt/config/transport/base'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Bolt
         
     | 
| 
      
 7 
     | 
    
         
            +
              class Config
         
     | 
| 
      
 8 
     | 
    
         
            +
                module Transport
         
     | 
| 
      
 9 
     | 
    
         
            +
                  class LXD < Base
         
     | 
| 
      
 10 
     | 
    
         
            +
                    OPTIONS = %w[
         
     | 
| 
      
 11 
     | 
    
         
            +
                      cleanup
         
     | 
| 
      
 12 
     | 
    
         
            +
                      tmpdir
         
     | 
| 
      
 13 
     | 
    
         
            +
                    ].freeze
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                    DEFAULTS = {
         
     | 
| 
      
 16 
     | 
    
         
            +
                      'cleanup' => true
         
     | 
| 
      
 17 
     | 
    
         
            +
                    }.freeze
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -32,7 +32,7 @@ module Bolt 
     | 
|
| 
       32 
32 
     | 
    
         
             
                      "cleanup" => {
         
     | 
| 
       33 
33 
     | 
    
         
             
                        type: [TrueClass, FalseClass],
         
     | 
| 
       34 
34 
     | 
    
         
             
                        description: "Whether to clean up temporary files created on targets. When running commands on a target, "\
         
     | 
| 
       35 
     | 
    
         
            -
                                     "Bolt  
     | 
| 
      
 35 
     | 
    
         
            +
                                     "Bolt might create temporary files. After completing the command, these files are "\
         
     | 
| 
       36 
36 
     | 
    
         
             
                                     "automatically deleted. This value can be set to 'false' if you wish to leave these "\
         
     | 
| 
       37 
37 
     | 
    
         
             
                                     "temporary files on the target.",
         
     | 
| 
       38 
38 
     | 
    
         
             
                        _plugin: true,
         
     | 
    
        data/lib/bolt/executor.rb
    CHANGED
    
    | 
         @@ -4,6 +4,7 @@ 
     | 
|
| 
       4 
4 
     | 
    
         
             
            require 'English'
         
     | 
| 
       5 
5 
     | 
    
         
             
            require 'json'
         
     | 
| 
       6 
6 
     | 
    
         
             
            require 'logging'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'pathname'
         
     | 
| 
       7 
8 
     | 
    
         
             
            require 'set'
         
     | 
| 
       8 
9 
     | 
    
         
             
            require 'bolt/analytics'
         
     | 
| 
       9 
10 
     | 
    
         
             
            require 'bolt/result'
         
     | 
| 
         @@ -15,6 +16,7 @@ require 'bolt/transport/ssh' 
     | 
|
| 
       15 
16 
     | 
    
         
             
            require 'bolt/transport/winrm'
         
     | 
| 
       16 
17 
     | 
    
         
             
            require 'bolt/transport/orch'
         
     | 
| 
       17 
18 
     | 
    
         
             
            require 'bolt/transport/local'
         
     | 
| 
      
 19 
     | 
    
         
            +
            require 'bolt/transport/lxd'
         
     | 
| 
       18 
20 
     | 
    
         
             
            require 'bolt/transport/docker'
         
     | 
| 
       19 
21 
     | 
    
         
             
            require 'bolt/transport/remote'
         
     | 
| 
       20 
22 
     | 
    
         
             
            require 'bolt/yarn'
         
     | 
| 
         @@ -25,6 +27,7 @@ module Bolt 
     | 
|
| 
       25 
27 
     | 
    
         
             
                winrm: Bolt::Transport::WinRM,
         
     | 
| 
       26 
28 
     | 
    
         
             
                pcp: Bolt::Transport::Orch,
         
     | 
| 
       27 
29 
     | 
    
         
             
                local: Bolt::Transport::Local,
         
     | 
| 
      
 30 
     | 
    
         
            +
                lxd: Bolt::Transport::LXD,
         
     | 
| 
       28 
31 
     | 
    
         
             
                docker: Bolt::Transport::Docker,
         
     | 
| 
       29 
32 
     | 
    
         
             
                remote: Bolt::Transport::Remote
         
     | 
| 
       30 
33 
     | 
    
         
             
              }.freeze
         
     | 
| 
         @@ -39,7 +42,6 @@ module Bolt 
     | 
|
| 
       39 
42 
     | 
    
         
             
                               modified_concurrency = false)
         
     | 
| 
       40 
43 
     | 
    
         
             
                  # lazy-load expensive gem code
         
     | 
| 
       41 
44 
     | 
    
         
             
                  require 'concurrent'
         
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
45 
     | 
    
         
             
                  @analytics = analytics
         
     | 
| 
       44 
46 
     | 
    
         
             
                  @logger = Bolt::Logger.logger(self)
         
     | 
| 
       45 
47 
     | 
    
         | 
| 
         @@ -121,8 +123,8 @@ module Bolt 
     | 
|
| 
       121 
123 
     | 
    
         
             
                def queue_execute(targets)
         
     | 
| 
       122 
124 
     | 
    
         
             
                  if @warn_concurrency && targets.length > @concurrency
         
     | 
| 
       123 
125 
     | 
    
         
             
                    @warn_concurrency = false
         
     | 
| 
       124 
     | 
    
         
            -
                    msg = "The ulimit is low, which  
     | 
| 
       125 
     | 
    
         
            -
                          "'#{@concurrency}' to mitigate those issues, which  
     | 
| 
      
 126 
     | 
    
         
            +
                    msg = "The ulimit is low, which might cause file limit issues. Default concurrency has been set to "\
         
     | 
| 
      
 127 
     | 
    
         
            +
                          "'#{@concurrency}' to mitigate those issues, which might cause Bolt to run slow. "\
         
     | 
| 
       126 
128 
     | 
    
         
             
                          "Disable this warning by configuring ulimit using 'ulimit -n <limit>' in your shell "\
         
     | 
| 
       127 
129 
     | 
    
         
             
                          "configuration, or by configuring Bolt's concurrency. "\
         
     | 
| 
       128 
130 
     | 
    
         
             
                          "See https://puppet.com/docs/bolt/latest/bolt_known_issues.html for details."
         
     | 
| 
         @@ -231,6 +233,11 @@ module Bolt 
     | 
|
| 
       231 
233 
     | 
    
         
             
                  @analytics.report_bundled_content(mode, name)
         
     | 
| 
       232 
234 
     | 
    
         
             
                end
         
     | 
| 
       233 
235 
     | 
    
         | 
| 
      
 236 
     | 
    
         
            +
                def report_file_source(plan_function, source)
         
     | 
| 
      
 237 
     | 
    
         
            +
                  label = Pathname.new(source).absolute? ? 'absolute' : 'module'
         
     | 
| 
      
 238 
     | 
    
         
            +
                  @analytics&.event('Plan', plan_function, label: label)
         
     | 
| 
      
 239 
     | 
    
         
            +
                end
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
       234 
241 
     | 
    
         
             
                def report_apply(statement_count, resource_counts)
         
     | 
| 
       235 
242 
     | 
    
         
             
                  data = { statement_count: statement_count }
         
     | 
| 
       236 
243 
     | 
    
         | 
    
        data/lib/bolt/logger.rb
    CHANGED
    
    | 
         @@ -91,6 +91,14 @@ module Bolt 
     | 
|
| 
       91 
91 
     | 
    
         
             
                  Logging.logger[:root].appenders.any?
         
     | 
| 
       92 
92 
     | 
    
         
             
                end
         
     | 
| 
       93 
93 
     | 
    
         | 
| 
      
 94 
     | 
    
         
            +
                def self.stream
         
     | 
| 
      
 95 
     | 
    
         
            +
                  @stream
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                def self.stream=(stream)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  @stream = stream
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
       94 
102 
     | 
    
         
             
                # A helper to ensure the Logging library is always initialized with our
         
     | 
| 
       95 
103 
     | 
    
         
             
                # custom log levels before retrieving a Logger instance.
         
     | 
| 
       96 
104 
     | 
    
         
             
                def self.logger(name)
         
     | 
| 
         @@ -45,7 +45,7 @@ module Bolt 
     | 
|
| 
       45 
45 
     | 
    
         
             
                  # specss. If that fails, fall back to resolving from project specs.
         
     | 
| 
       46 
46 
     | 
    
         
             
                  # This prevents Bolt from modifying installed modules unless there is
         
     | 
| 
       47 
47 
     | 
    
         
             
                  # a version conflict.
         
     | 
| 
       48 
     | 
    
         
            -
                  @outputter.print_action_step("Resolving module dependencies, this  
     | 
| 
      
 48 
     | 
    
         
            +
                  @outputter.print_action_step("Resolving module dependencies, this might take a moment")
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
50 
     | 
    
         
             
                  @outputter.start_spin
         
     | 
| 
       51 
51 
     | 
    
         
             
                  begin
         
     | 
| 
         @@ -156,7 +156,7 @@ module Bolt 
     | 
|
| 
       156 
156 
     | 
    
         
             
                    # If forcibly installing or if there is no Puppetfile, resolve
         
     | 
| 
       157 
157 
     | 
    
         
             
                    # and write a Puppetfile.
         
     | 
| 
       158 
158 
     | 
    
         
             
                    if force || !path.exist?
         
     | 
| 
       159 
     | 
    
         
            -
                      @outputter.print_action_step("Resolving module dependencies, this  
     | 
| 
      
 159 
     | 
    
         
            +
                      @outputter.print_action_step("Resolving module dependencies, this might take a moment")
         
     | 
| 
       160 
160 
     | 
    
         | 
| 
       161 
161 
     | 
    
         
             
                      # This doesn't use the block as it's more testable to just mock *_spin
         
     | 
| 
       162 
162 
     | 
    
         
             
                      @outputter.start_spin
         
     | 
| 
         @@ -36,7 +36,7 @@ module Bolt 
     | 
|
| 
       36 
36 
     | 
    
         
             
                      raise Bolt::ValidationError, <<~MSG
         
     | 
| 
       37 
37 
     | 
    
         
             
                        Unable to parse Puppetfile #{path}:
         
     | 
| 
       38 
38 
     | 
    
         
             
                        #{parsed.validation_errors.join("\n\n")}.
         
     | 
| 
       39 
     | 
    
         
            -
                        This  
     | 
| 
      
 39 
     | 
    
         
            +
                        This Puppetfile might not be managed by Bolt.
         
     | 
| 
       40 
40 
     | 
    
         
             
                      MSG
         
     | 
| 
       41 
41 
     | 
    
         
             
                    end
         
     | 
| 
       42 
42 
     | 
    
         | 
| 
         @@ -106,7 +106,7 @@ module Bolt 
     | 
|
| 
       106 
106 
     | 
    
         | 
| 
       107 
107 
     | 
    
         
             
                      #{unsatisfied_specs.map(&:to_hash).to_yaml.lines.drop(1).join.chomp}
         
     | 
| 
       108 
108 
     | 
    
         | 
| 
       109 
     | 
    
         
            -
                      This  
     | 
| 
      
 109 
     | 
    
         
            +
                      This Puppetfile might not be managed by Bolt. To forcibly overwrite the
         
     | 
| 
       110 
110 
     | 
    
         
             
                      Puppetfile, run '#{command}'.
         
     | 
| 
       111 
111 
     | 
    
         
             
                    MESSAGE
         
     | 
| 
       112 
112 
     | 
    
         | 
| 
         @@ -39,8 +39,8 @@ module Bolt 
     | 
|
| 
       39 
39 
     | 
    
         
             
                      unless (match = name.match(NAME_REGEX))
         
     | 
| 
       40 
40 
     | 
    
         
             
                        raise Bolt::ValidationError,
         
     | 
| 
       41 
41 
     | 
    
         
             
                              "Invalid name for Forge module specification: #{name}. Name must match "\
         
     | 
| 
       42 
     | 
    
         
            -
                              "'owner/name'. Owner segment  
     | 
| 
       43 
     | 
    
         
            -
                              "segment must start with a lowercase letter and  
     | 
| 
      
 42 
     | 
    
         
            +
                              "'owner/name'. Owner segment can only include letters or digits. Name "\
         
     | 
| 
      
 43 
     | 
    
         
            +
                              "segment must start with a lowercase letter and can only include lowercase "\
         
     | 
| 
       44 
44 
     | 
    
         
             
                              "letters, digits, and underscores."
         
     | 
| 
       45 
45 
     | 
    
         
             
                      end
         
     | 
| 
       46 
46 
     | 
    
         | 
| 
         @@ -49,8 +49,8 @@ module Bolt 
     | 
|
| 
       49 
49 
     | 
    
         
             
                      unless (match = name.match(NAME_REGEX))
         
     | 
| 
       50 
50 
     | 
    
         
             
                        raise Bolt::ValidationError,
         
     | 
| 
       51 
51 
     | 
    
         
             
                              "Invalid name for Git module specification: #{name}. Name must match "\
         
     | 
| 
       52 
     | 
    
         
            -
                              "'name' or 'owner/name'. Owner segment  
     | 
| 
       53 
     | 
    
         
            -
                              "Name segment must start with a lowercase letter and  
     | 
| 
      
 52 
     | 
    
         
            +
                              "'name' or 'owner/name'. Owner segment can only include letters or digits. "\
         
     | 
| 
      
 53 
     | 
    
         
            +
                              "Name segment must start with a lowercase letter and can only include "\
         
     | 
| 
       54 
54 
     | 
    
         
             
                              "lowercase letters, digits, and underscores."
         
     | 
| 
       55 
55 
     | 
    
         
             
                      end
         
     | 
| 
       56 
56 
     | 
    
         | 
    
        data/lib/bolt/outputter/human.rb
    CHANGED
    
    | 
         @@ -53,10 +53,21 @@ module Bolt 
     | 
|
| 
       53 
53 
     | 
    
         
             
                    string.sub(/\s\z/, '')
         
     | 
| 
       54 
54 
     | 
    
         
             
                  end
         
     | 
| 
       55 
55 
     | 
    
         | 
| 
      
 56 
     | 
    
         
            +
                  # Wraps a string to the specified width. Lines only wrap
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # at whitespace.
         
     | 
| 
      
 58 
     | 
    
         
            +
                  #
         
     | 
| 
       56 
59 
     | 
    
         
             
                  def wrap(string, width = 80)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    return string unless string.is_a?(String)
         
     | 
| 
       57 
61 
     | 
    
         
             
                    string.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n")
         
     | 
| 
       58 
62 
     | 
    
         
             
                  end
         
     | 
| 
       59 
63 
     | 
    
         | 
| 
      
 64 
     | 
    
         
            +
                  # Trims a string to a specified width, adding an ellipsis if it's longer.
         
     | 
| 
      
 65 
     | 
    
         
            +
                  #
         
     | 
| 
      
 66 
     | 
    
         
            +
                  def truncate(string, width = 80)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    return string unless string.is_a?(String) && string.length > width
         
     | 
| 
      
 68 
     | 
    
         
            +
                    string.lines.first[0...width].gsub(/\s\w+\s*$/, '...')
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
       60 
71 
     | 
    
         
             
                  def handle_event(event)
         
     | 
| 
       61 
72 
     | 
    
         
             
                    case event[:type]
         
     | 
| 
       62 
73 
     | 
    
         
             
                    when :enable_default_output
         
     | 
| 
         @@ -218,11 +229,11 @@ module Bolt 
     | 
|
| 
       218 
229 
     | 
    
         
             
                    @stream.puts total_msg
         
     | 
| 
       219 
230 
     | 
    
         
             
                  end
         
     | 
| 
       220 
231 
     | 
    
         | 
| 
       221 
     | 
    
         
            -
                  def  
     | 
| 
      
 232 
     | 
    
         
            +
                  def format_table(results, padding_left = 0, padding_right = 3)
         
     | 
| 
       222 
233 
     | 
    
         
             
                    # lazy-load expensive gem code
         
     | 
| 
       223 
234 
     | 
    
         
             
                    require 'terminal-table'
         
     | 
| 
       224 
235 
     | 
    
         | 
| 
       225 
     | 
    
         
            -
                     
     | 
| 
      
 236 
     | 
    
         
            +
                    Terminal::Table.new(
         
     | 
| 
       226 
237 
     | 
    
         
             
                      rows: results,
         
     | 
| 
       227 
238 
     | 
    
         
             
                      style: {
         
     | 
| 
       228 
239 
     | 
    
         
             
                        border_x: '',
         
     | 
| 
         @@ -238,10 +249,22 @@ module Bolt 
     | 
|
| 
       238 
249 
     | 
    
         | 
| 
       239 
250 
     | 
    
         
             
                  def print_tasks(tasks, modulepath)
         
     | 
| 
       240 
251 
     | 
    
         
             
                    command = Bolt::Util.powershell? ? 'Get-BoltTask -Task <TASK NAME>' : 'bolt task show <TASK NAME>'
         
     | 
| 
       241 
     | 
    
         
            -
             
     | 
| 
       242 
     | 
    
         
            -
                     
     | 
| 
       243 
     | 
    
         
            -
             
     | 
| 
       244 
     | 
    
         
            -
             
     | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
      
 253 
     | 
    
         
            +
                    tasks = tasks.map do |name, description|
         
     | 
| 
      
 254 
     | 
    
         
            +
                      description = truncate(description, 72)
         
     | 
| 
      
 255 
     | 
    
         
            +
                      [name, description]
         
     | 
| 
      
 256 
     | 
    
         
            +
                    end
         
     | 
| 
      
 257 
     | 
    
         
            +
             
     | 
| 
      
 258 
     | 
    
         
            +
                    @stream.puts colorize(:cyan, 'Tasks')
         
     | 
| 
      
 259 
     | 
    
         
            +
                    @stream.puts tasks.any? ? format_table(tasks, 2) : indent(2, 'No available tasks')
         
     | 
| 
      
 260 
     | 
    
         
            +
                    @stream.puts
         
     | 
| 
      
 261 
     | 
    
         
            +
             
     | 
| 
      
 262 
     | 
    
         
            +
                    @stream.puts colorize(:cyan, 'Modulepath')
         
     | 
| 
      
 263 
     | 
    
         
            +
                    @stream.puts indent(2, modulepath.join(File::PATH_SEPARATOR))
         
     | 
| 
      
 264 
     | 
    
         
            +
                    @stream.puts
         
     | 
| 
      
 265 
     | 
    
         
            +
             
     | 
| 
      
 266 
     | 
    
         
            +
                    @stream.puts colorize(:cyan, 'Additional information')
         
     | 
| 
      
 267 
     | 
    
         
            +
                    @stream.puts indent(2, "Use '#{command}' to view details and parameters for a specific task.")
         
     | 
| 
       245 
268 
     | 
    
         
             
                  end
         
     | 
| 
       246 
269 
     | 
    
         | 
| 
       247 
270 
     | 
    
         
             
                  # @param [Hash] task A hash representing the task
         
     | 
| 
         @@ -322,10 +345,22 @@ module Bolt 
     | 
|
| 
       322 
345 
     | 
    
         | 
| 
       323 
346 
     | 
    
         
             
                  def print_plans(plans, modulepath)
         
     | 
| 
       324 
347 
     | 
    
         
             
                    command = Bolt::Util.powershell? ? 'Get-BoltPlan -Name <PLAN NAME>' : 'bolt plan show <PLAN NAME>'
         
     | 
| 
       325 
     | 
    
         
            -
             
     | 
| 
       326 
     | 
    
         
            -
                     
     | 
| 
       327 
     | 
    
         
            -
             
     | 
| 
       328 
     | 
    
         
            -
             
     | 
| 
      
 348 
     | 
    
         
            +
             
     | 
| 
      
 349 
     | 
    
         
            +
                    plans = plans.map do |name, description|
         
     | 
| 
      
 350 
     | 
    
         
            +
                      description = truncate(description, 72)
         
     | 
| 
      
 351 
     | 
    
         
            +
                      [name, description]
         
     | 
| 
      
 352 
     | 
    
         
            +
                    end
         
     | 
| 
      
 353 
     | 
    
         
            +
             
     | 
| 
      
 354 
     | 
    
         
            +
                    @stream.puts colorize(:cyan, 'Plans')
         
     | 
| 
      
 355 
     | 
    
         
            +
                    @stream.puts plans.any? ? format_table(plans, 2) : indent(2, 'No available plans')
         
     | 
| 
      
 356 
     | 
    
         
            +
                    @stream.puts
         
     | 
| 
      
 357 
     | 
    
         
            +
             
     | 
| 
      
 358 
     | 
    
         
            +
                    @stream.puts colorize(:cyan, 'Modulepath')
         
     | 
| 
      
 359 
     | 
    
         
            +
                    @stream.puts indent(2, modulepath.join(File::PATH_SEPARATOR))
         
     | 
| 
      
 360 
     | 
    
         
            +
                    @stream.puts
         
     | 
| 
      
 361 
     | 
    
         
            +
             
     | 
| 
      
 362 
     | 
    
         
            +
                    @stream.puts colorize(:cyan, 'Additional information')
         
     | 
| 
      
 363 
     | 
    
         
            +
                    @stream.puts indent(2, "Use '#{command}' to view details and parameters for a specific plan.")
         
     | 
| 
       329 
364 
     | 
    
         
             
                  end
         
     | 
| 
       330 
365 
     | 
    
         | 
| 
       331 
366 
     | 
    
         
             
                  def print_topics(topics)
         
     | 
| 
         @@ -359,7 +394,7 @@ module Bolt 
     | 
|
| 
       359 
394 
     | 
    
         
             
                          [m[:name], version]
         
     | 
| 
       360 
395 
     | 
    
         
             
                        end
         
     | 
| 
       361 
396 
     | 
    
         | 
| 
       362 
     | 
    
         
            -
                         
     | 
| 
      
 397 
     | 
    
         
            +
                        @stream.puts format_table(module_info, 2, 1)
         
     | 
| 
       363 
398 
     | 
    
         
             
                      end
         
     | 
| 
       364 
399 
     | 
    
         | 
| 
       365 
400 
     | 
    
         
             
                      @stream.write("\n")
         
     | 
| 
         @@ -374,7 +409,7 @@ module Bolt 
     | 
|
| 
       374 
409 
     | 
    
         
             
                    targets += target_list[:adhoc].map { |target| [target.name, adhoc] }
         
     | 
| 
       375 
410 
     | 
    
         | 
| 
       376 
411 
     | 
    
         
             
                    if targets.any?
         
     | 
| 
       377 
     | 
    
         
            -
                       
     | 
| 
      
 412 
     | 
    
         
            +
                      @stream.puts format_table(targets, 0, 2)
         
     | 
| 
       378 
413 
     | 
    
         
             
                      @stream.puts
         
     | 
| 
       379 
414 
     | 
    
         
             
                    end
         
     | 
| 
       380 
415 
     | 
    
         | 
    
        data/lib/bolt/pal.rb
    CHANGED
    
    | 
         @@ -385,7 +385,7 @@ module Bolt 
     | 
|
| 
       385 
385 
     | 
    
         
             
                      plan_cache[plan_name] = info
         
     | 
| 
       386 
386 
     | 
    
         
             
                    end
         
     | 
| 
       387 
387 
     | 
    
         | 
| 
       388 
     | 
    
         
            -
                    list << [plan_name] unless info['private']
         
     | 
| 
      
 388 
     | 
    
         
            +
                    list << [plan_name, info['description']] unless info['private']
         
     | 
| 
       389 
389 
     | 
    
         
             
                  end
         
     | 
| 
       390 
390 
     | 
    
         | 
| 
       391 
391 
     | 
    
         
             
                  File.write(@project.plan_cache_file, plan_cache.to_json) if updated
         
     | 
| 
         @@ -446,7 +446,7 @@ module Bolt 
     | 
|
| 
       446 
446 
     | 
    
         
             
                        params[name] = { 'type' => param.types.first }
         
     | 
| 
       447 
447 
     | 
    
         
             
                        params[name]['sensitive'] = param.types.first =~ /\ASensitive(\[.*\])?\z/ ? true : false
         
     | 
| 
       448 
448 
     | 
    
         
             
                        params[name]['default_value'] = defaults[name] if defaults.key?(name)
         
     | 
| 
       449 
     | 
    
         
            -
                        params[name]['description'] = param.text  
     | 
| 
      
 449 
     | 
    
         
            +
                        params[name]['description'] = param.text if param.text && !param.text.empty?
         
     | 
| 
       450 
450 
     | 
    
         
             
                      else
         
     | 
| 
       451 
451 
     | 
    
         
             
                        Bolt::Logger.warn(
         
     | 
| 
       452 
452 
     | 
    
         
             
                          "missing_plan_parameter",
         
     | 
    
        data/lib/bolt/pal/yaml_plan.rb
    CHANGED
    
    | 
         @@ -73,8 +73,7 @@ module Bolt 
     | 
|
| 
       73 
73 
     | 
    
         
             
                  def duplicate_check(used_names, name, step_number)
         
     | 
| 
       74 
74 
     | 
    
         
             
                    if used_names.include?(name)
         
     | 
| 
       75 
75 
     | 
    
         
             
                      error_message = "Duplicate step name or parameter detected: #{name.inspect}"
         
     | 
| 
       76 
     | 
    
         
            -
                       
     | 
| 
       77 
     | 
    
         
            -
                      raise Bolt::Error.new(err, "bolt/invalid-plan")
         
     | 
| 
      
 76 
     | 
    
         
            +
                      raise Step::StepError.new(error_message, name, step_number)
         
     | 
| 
       78 
77 
     | 
    
         
             
                    end
         
     | 
| 
       79 
78 
     | 
    
         
             
                  end
         
     | 
| 
       80 
79 
     | 
    
         | 
| 
         @@ -12,153 +12,15 @@ module Bolt 
     | 
|
| 
       12 
12 
     | 
    
         
             
                      @evaluator = Puppet::Pops::Parser::EvaluatingParser.new
         
     | 
| 
       13 
13 
     | 
    
         
             
                    end
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
                    def dispatch_step(scope, step)
         
     | 
| 
       16 
     | 
    
         
            -
                      step_body = evaluate_code_blocks(scope, step.body)
         
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
                      # Dispatch based on the step class name
         
     | 
| 
       19 
     | 
    
         
            -
                      step_type = step.class.name.split('::').last.downcase
         
     | 
| 
       20 
     | 
    
         
            -
                      method = "#{step_type}_step"
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
                      send(method, scope, step_body)
         
     | 
| 
       23 
     | 
    
         
            -
                    end
         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
                    def task_step(scope, step)
         
     | 
| 
       26 
     | 
    
         
            -
                      task = step['task']
         
     | 
| 
       27 
     | 
    
         
            -
                      targets = step['targets']
         
     | 
| 
       28 
     | 
    
         
            -
                      description = step['description']
         
     | 
| 
       29 
     | 
    
         
            -
                      params = step['parameters'] || {}
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
                      args = if description
         
     | 
| 
       32 
     | 
    
         
            -
                               [task, targets, description, params]
         
     | 
| 
       33 
     | 
    
         
            -
                             else
         
     | 
| 
       34 
     | 
    
         
            -
                               [task, targets, params]
         
     | 
| 
       35 
     | 
    
         
            -
                             end
         
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
                      scope.call_function('run_task', args)
         
     | 
| 
       38 
     | 
    
         
            -
                    end
         
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
                    def plan_step(scope, step)
         
     | 
| 
       41 
     | 
    
         
            -
                      plan = step['plan']
         
     | 
| 
       42 
     | 
    
         
            -
                      parameters = step['parameters'] || {}
         
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
                      args = [plan, parameters]
         
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
                      scope.call_function('run_plan', args)
         
     | 
| 
       47 
     | 
    
         
            -
                    end
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
                    def script_step(scope, step)
         
     | 
| 
       50 
     | 
    
         
            -
                      script = step['script']
         
     | 
| 
       51 
     | 
    
         
            -
                      targets = step['targets']
         
     | 
| 
       52 
     | 
    
         
            -
                      description = step['description']
         
     | 
| 
       53 
     | 
    
         
            -
                      arguments = step['arguments'] || []
         
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
                      options = { 'arguments' => arguments }
         
     | 
| 
       56 
     | 
    
         
            -
                      args = if description
         
     | 
| 
       57 
     | 
    
         
            -
                               [script, targets, description, options]
         
     | 
| 
       58 
     | 
    
         
            -
                             else
         
     | 
| 
       59 
     | 
    
         
            -
                               [script, targets, options]
         
     | 
| 
       60 
     | 
    
         
            -
                             end
         
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
                      scope.call_function('run_script', args)
         
     | 
| 
       63 
     | 
    
         
            -
                    end
         
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                    def command_step(scope, step)
         
     | 
| 
       66 
     | 
    
         
            -
                      command = step['command']
         
     | 
| 
       67 
     | 
    
         
            -
                      targets = step['targets']
         
     | 
| 
       68 
     | 
    
         
            -
                      description = step['description']
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
                      args = [command, targets]
         
     | 
| 
       71 
     | 
    
         
            -
                      args << description if description
         
     | 
| 
       72 
     | 
    
         
            -
                      scope.call_function('run_command', args)
         
     | 
| 
       73 
     | 
    
         
            -
                    end
         
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
                    def upload_step(scope, step)
         
     | 
| 
       76 
     | 
    
         
            -
                      source = step['upload']
         
     | 
| 
       77 
     | 
    
         
            -
                      destination = step['destination']
         
     | 
| 
       78 
     | 
    
         
            -
                      targets = step['targets']
         
     | 
| 
       79 
     | 
    
         
            -
                      description = step['description']
         
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
       81 
     | 
    
         
            -
                      args = [source, destination, targets]
         
     | 
| 
       82 
     | 
    
         
            -
                      args << description if description
         
     | 
| 
       83 
     | 
    
         
            -
                      scope.call_function('upload_file', args)
         
     | 
| 
       84 
     | 
    
         
            -
                    end
         
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
                    def download_step(scope, step)
         
     | 
| 
       87 
     | 
    
         
            -
                      source = step['download']
         
     | 
| 
       88 
     | 
    
         
            -
                      destination = step['destination']
         
     | 
| 
       89 
     | 
    
         
            -
                      targets = step['targets']
         
     | 
| 
       90 
     | 
    
         
            -
                      description = step['description']
         
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
                      args = [source, destination, targets]
         
     | 
| 
       93 
     | 
    
         
            -
                      args << description if description
         
     | 
| 
       94 
     | 
    
         
            -
                      scope.call_function('download_file', args)
         
     | 
| 
       95 
     | 
    
         
            -
                    end
         
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
       97 
     | 
    
         
            -
                    def eval_step(_scope, step)
         
     | 
| 
       98 
     | 
    
         
            -
                      step['eval']
         
     | 
| 
       99 
     | 
    
         
            -
                    end
         
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
       101 
     | 
    
         
            -
                    def resources_step(scope, step)
         
     | 
| 
       102 
     | 
    
         
            -
                      targets = step['targets']
         
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
                      # TODO: Only call apply_prep when needed
         
     | 
| 
       105 
     | 
    
         
            -
                      scope.call_function('apply_prep', targets)
         
     | 
| 
       106 
     | 
    
         
            -
                      manifest = generate_manifest(step['resources'])
         
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
       108 
     | 
    
         
            -
                      apply_manifest(scope, targets, manifest)
         
     | 
| 
       109 
     | 
    
         
            -
                    end
         
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
                    def message_step(scope, step)
         
     | 
| 
       112 
     | 
    
         
            -
                      scope.call_function('out::message', [step['message']])
         
     | 
| 
       113 
     | 
    
         
            -
                    end
         
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
                    def generate_manifest(resources)
         
     | 
| 
       116 
     | 
    
         
            -
                      # inspect returns the Ruby representation of the resource hashes,
         
     | 
| 
       117 
     | 
    
         
            -
                      # which happens to be the same as the Puppet representation
         
     | 
| 
       118 
     | 
    
         
            -
                      puppet_resources = resources.inspect
         
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
     | 
    
         
            -
                      # Because the :tasks setting globally controls which mode the parser
         
     | 
| 
       121 
     | 
    
         
            -
                      # is in, we need to make this snippet of non-tasks manifest code
         
     | 
| 
       122 
     | 
    
         
            -
                      # parseable in tasks mode. The way to do that is by putting it in an
         
     | 
| 
       123 
     | 
    
         
            -
                      # apply statement and taking the body.
         
     | 
| 
       124 
     | 
    
         
            -
                      <<~MANIFEST
         
     | 
| 
       125 
     | 
    
         
            -
                      apply('placeholder') {
         
     | 
| 
       126 
     | 
    
         
            -
                        $resources = #{puppet_resources}
         
     | 
| 
       127 
     | 
    
         
            -
                        $resources.each |$res| {
         
     | 
| 
       128 
     | 
    
         
            -
                          Resource[$res['type']] { $res['title']:
         
     | 
| 
       129 
     | 
    
         
            -
                            * => $res['parameters'],
         
     | 
| 
       130 
     | 
    
         
            -
                          }
         
     | 
| 
       131 
     | 
    
         
            -
                        }
         
     | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
                        # Add relationships if there is more than one resource
         
     | 
| 
       134 
     | 
    
         
            -
                        if $resources.length > 1 {
         
     | 
| 
       135 
     | 
    
         
            -
                          ($resources.length - 1).each |$index| {
         
     | 
| 
       136 
     | 
    
         
            -
                            $lhs = $resources[$index]
         
     | 
| 
       137 
     | 
    
         
            -
                            $rhs = $resources[$index+1]
         
     | 
| 
       138 
     | 
    
         
            -
                            $lhs_resource = Resource[$lhs['type'] , $lhs['title']]
         
     | 
| 
       139 
     | 
    
         
            -
                            $rhs_resource = Resource[$rhs['type'] , $rhs['title']]
         
     | 
| 
       140 
     | 
    
         
            -
                            $lhs_resource -> $rhs_resource
         
     | 
| 
       141 
     | 
    
         
            -
                          }
         
     | 
| 
       142 
     | 
    
         
            -
                        }
         
     | 
| 
       143 
     | 
    
         
            -
                      }
         
     | 
| 
       144 
     | 
    
         
            -
                      MANIFEST
         
     | 
| 
       145 
     | 
    
         
            -
                    end
         
     | 
| 
       146 
     | 
    
         
            -
             
     | 
| 
       147 
     | 
    
         
            -
                    def apply_manifest(scope, targets, manifest)
         
     | 
| 
       148 
     | 
    
         
            -
                      ast = @evaluator.parse_string(manifest)
         
     | 
| 
       149 
     | 
    
         
            -
                      apply_block = ast.body.body
         
     | 
| 
       150 
     | 
    
         
            -
                      applicator = Puppet.lookup(:apply_executor)
         
     | 
| 
       151 
     | 
    
         
            -
                      applicator.apply([targets], apply_block, scope)
         
     | 
| 
       152 
     | 
    
         
            -
                    end
         
     | 
| 
       153 
     | 
    
         
            -
             
     | 
| 
       154 
15 
     | 
    
         
             
                    # This is the method that Puppet calls to evaluate the plan. The name
         
     | 
| 
       155 
16 
     | 
    
         
             
                    # makes more sense for .pp plans.
         
     | 
| 
      
 17 
     | 
    
         
            +
                    #
         
     | 
| 
       156 
18 
     | 
    
         
             
                    def evaluate_block_with_bindings(closure_scope, args_hash, plan)
         
     | 
| 
       157 
19 
     | 
    
         
             
                      plan_result = closure_scope.with_local_scope(args_hash) do |scope|
         
     | 
| 
       158 
20 
     | 
    
         
             
                        plan.steps.each do |step|
         
     | 
| 
       159 
     | 
    
         
            -
                          step_result =  
     | 
| 
      
 21 
     | 
    
         
            +
                          step_result = step.evaluate(scope, self)
         
     | 
| 
       160 
22 
     | 
    
         | 
| 
       161 
     | 
    
         
            -
                          scope.setvar(step.name, step_result) if step.name
         
     | 
| 
      
 23 
     | 
    
         
            +
                          scope.setvar(step.body['name'], step_result) if step.body['name']
         
     | 
| 
       162 
24 
     | 
    
         
             
                        end
         
     | 
| 
       163 
25 
     | 
    
         | 
| 
       164 
26 
     | 
    
         
             
                        evaluate_code_blocks(scope, plan.return)
         
     | 
| 
         @@ -168,6 +30,7 @@ module Bolt 
     | 
|
| 
       168 
30 
     | 
    
         
             
                    end
         
     | 
| 
       169 
31 
     | 
    
         | 
| 
       170 
32 
     | 
    
         
             
                    # Recursively evaluate any EvaluableString instances in the object.
         
     | 
| 
      
 33 
     | 
    
         
            +
                    #
         
     | 
| 
       171 
34 
     | 
    
         
             
                    def evaluate_code_blocks(scope, value)
         
     | 
| 
       172 
35 
     | 
    
         
             
                      # XXX We should establish a local scope here probably
         
     | 
| 
       173 
36 
     | 
    
         
             
                      case value
         
     | 
| 
         @@ -192,6 +55,7 @@ module Bolt 
     | 
|
| 
       192 
55 
     | 
    
         
             
                    # Occasionally the Closure will ask us to evaluate what it assumes are
         
     | 
| 
       193 
56 
     | 
    
         
             
                    # AST objects. Because we've sidestepped the AST, they aren't, so just
         
     | 
| 
       194 
57 
     | 
    
         
             
                    # return the values as already evaluated.
         
     | 
| 
      
 58 
     | 
    
         
            +
                    #
         
     | 
| 
       195 
59 
     | 
    
         
             
                    def evaluate(value, _scope)
         
     | 
| 
       196 
60 
     | 
    
         
             
                      value
         
     | 
| 
       197 
61 
     | 
    
         
             
                    end
         
     |