bolt 2.7.0 → 2.11.1
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 +4 -3
- data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +27 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +4 -3
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +192 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +122 -0
- data/bolt-modules/boltlib/types/planresult.pp +12 -1
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +3 -1
- data/bolt-modules/file/lib/puppet/functions/file/join.rb +1 -1
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +2 -1
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +3 -1
- data/bolt-modules/file/lib/puppet/functions/file/write.rb +3 -1
- data/lib/bolt/applicator.rb +3 -2
- data/lib/bolt/apply_inventory.rb +1 -1
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/apply_target.rb +11 -2
- data/lib/bolt/bolt_option_parser.rb +22 -6
- data/lib/bolt/cli.rb +52 -22
- data/lib/bolt/config.rb +57 -27
- data/lib/bolt/config/transport/base.rb +3 -3
- data/lib/bolt/config/transport/docker.rb +2 -0
- data/lib/bolt/config/transport/local.rb +2 -0
- data/lib/bolt/config/transport/orch.rb +4 -2
- data/lib/bolt/config/transport/remote.rb +2 -0
- data/lib/bolt/config/transport/ssh.rb +51 -2
- data/lib/bolt/config/transport/winrm.rb +3 -1
- data/lib/bolt/executor.rb +16 -0
- data/lib/bolt/inventory.rb +2 -1
- data/lib/bolt/inventory/group.rb +1 -0
- data/lib/bolt/inventory/inventory.rb +5 -0
- data/lib/bolt/inventory/target.rb +17 -1
- data/lib/bolt/node/output.rb +1 -1
- data/lib/bolt/outputter/human.rb +5 -4
- data/lib/bolt/outputter/json.rb +1 -1
- data/lib/bolt/pal.rb +32 -14
- data/lib/bolt/pal/yaml_plan.rb +1 -0
- data/lib/bolt/plugin.rb +14 -8
- data/lib/bolt/plugin/module.rb +40 -7
- data/lib/bolt/plugin/puppetdb.rb +5 -2
- data/lib/bolt/project.rb +135 -0
- data/lib/bolt/puppetdb/config.rb +16 -28
- data/lib/bolt/rerun.rb +1 -1
- data/lib/bolt/resource_instance.rb +126 -0
- data/lib/bolt/result.rb +46 -23
- data/lib/bolt/result_set.rb +2 -5
- data/lib/bolt/secret.rb +20 -4
- data/lib/bolt/shell/bash.rb +12 -5
- data/lib/bolt/shell/powershell.rb +12 -4
- data/lib/bolt/target.rb +16 -1
- data/lib/bolt/transport/base.rb +24 -8
- data/lib/bolt/transport/orch.rb +4 -0
- data/lib/bolt/transport/ssh.rb +6 -2
- data/lib/bolt/transport/ssh/connection.rb +4 -0
- data/lib/bolt/transport/ssh/exec_connection.rb +110 -0
- data/lib/bolt/transport/winrm/connection.rb +6 -2
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/pe/pal.rb +1 -38
- data/lib/bolt_server/transport_app.rb +7 -7
- data/lib/bolt_spec/bolt_context.rb +3 -6
- data/lib/bolt_spec/plans.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +1 -0
- data/lib/bolt_spec/run.rb +10 -13
- metadata +10 -7
- data/lib/bolt/boltdir.rb +0 -54
- data/lib/bolt/plugin/pkcs7.rb +0 -104
- data/lib/bolt/secret/base.rb +0 -41
| @@ -9,12 +9,12 @@ module Bolt | |
| 9 9 | 
             
                  class Base
         | 
| 10 10 | 
             
                    attr_reader :input
         | 
| 11 11 |  | 
| 12 | 
            -
                    def initialize(data = {},  | 
| 12 | 
            +
                    def initialize(data = {}, project = nil)
         | 
| 13 13 | 
             
                      assert_hash_or_config(data)
         | 
| 14 14 | 
             
                      @input    = data
         | 
| 15 15 | 
             
                      @resolved = !Bolt::Util.references?(input)
         | 
| 16 16 | 
             
                      @config   = resolved? ? Bolt::Util.deep_merge(defaults, filter(input)) : defaults
         | 
| 17 | 
            -
                      @ | 
| 17 | 
            +
                      @project  = project
         | 
| 18 18 |  | 
| 19 19 | 
             
                      validate if resolved?
         | 
| 20 20 | 
             
                    end
         | 
| @@ -62,7 +62,7 @@ module Bolt | |
| 62 62 | 
             
                        Bolt::Util.deep_merge(acc, layer_data)
         | 
| 63 63 | 
             
                      end
         | 
| 64 64 |  | 
| 65 | 
            -
                      self.class.new(merged, @ | 
| 65 | 
            +
                      self.class.new(merged, @project)
         | 
| 66 66 | 
             
                    end
         | 
| 67 67 |  | 
| 68 68 | 
             
                    # Resolve any references in the input data, then remerge it with the defaults
         | 
| @@ -7,6 +7,8 @@ module Bolt | |
| 7 7 | 
             
              class Config
         | 
| 8 8 | 
             
                module Transport
         | 
| 9 9 | 
             
                  class Docker < Base
         | 
| 10 | 
            +
                    # NOTE: All transport configuration options should have a corresponding schema definition
         | 
| 11 | 
            +
                    #       in schemas/bolt-transport-definitions.json
         | 
| 10 12 | 
             
                    OPTIONS = {
         | 
| 11 13 | 
             
                      "cleanup"       => { type: TrueClass,
         | 
| 12 14 | 
             
                                           desc: "Whether to clean up temporary files created on targets." },
         | 
| @@ -7,6 +7,8 @@ module Bolt | |
| 7 7 | 
             
              class Config
         | 
| 8 8 | 
             
                module Transport
         | 
| 9 9 | 
             
                  class Local < Base
         | 
| 10 | 
            +
                    # NOTE: All transport configuration options should have a corresponding schema definition
         | 
| 11 | 
            +
                    #       in schemas/bolt-transport-definitions.json
         | 
| 10 12 | 
             
                    OPTIONS = {
         | 
| 11 13 | 
             
                      "cleanup"         => { type: TrueClass,
         | 
| 12 14 | 
             
                                             desc: "Whether to clean up temporary files created on targets." },
         | 
| @@ -7,6 +7,8 @@ module Bolt | |
| 7 7 | 
             
              class Config
         | 
| 8 8 | 
             
                module Transport
         | 
| 9 9 | 
             
                  class Orch < Base
         | 
| 10 | 
            +
                    # NOTE: All transport configuration options should have a corresponding schema definition
         | 
| 11 | 
            +
                    #       in schemas/bolt-transport-definitions.json
         | 
| 10 12 | 
             
                    OPTIONS = {
         | 
| 11 13 | 
             
                      "cacert"            => { type: String,
         | 
| 12 14 | 
             
                                               desc: "The path to the CA certificate." },
         | 
| @@ -32,12 +34,12 @@ module Bolt | |
| 32 34 | 
             
                      super
         | 
| 33 35 |  | 
| 34 36 | 
             
                      if @config['cacert']
         | 
| 35 | 
            -
                        @config['cacert'] = File.expand_path(@config['cacert'], @ | 
| 37 | 
            +
                        @config['cacert'] = File.expand_path(@config['cacert'], @project)
         | 
| 36 38 | 
             
                        Bolt::Util.validate_file('cacert', @config['cacert'])
         | 
| 37 39 | 
             
                      end
         | 
| 38 40 |  | 
| 39 41 | 
             
                      if @config['token-file']
         | 
| 40 | 
            -
                        @config['token-file'] = File.expand_path(@config['token-file'], @ | 
| 42 | 
            +
                        @config['token-file'] = File.expand_path(@config['token-file'], @project)
         | 
| 41 43 | 
             
                        Bolt::Util.validate_file('token-file', @config['token-file'])
         | 
| 42 44 | 
             
                      end
         | 
| 43 45 | 
             
                    end
         | 
| @@ -7,6 +7,8 @@ module Bolt | |
| 7 7 | 
             
              class Config
         | 
| 8 8 | 
             
                module Transport
         | 
| 9 9 | 
             
                  class Remote < Base
         | 
| 10 | 
            +
                    # NOTE: All transport configuration options should have a corresponding schema definition
         | 
| 11 | 
            +
                    #       in schemas/bolt-transport-definitions.json
         | 
| 10 12 | 
             
                    OPTIONS = {
         | 
| 11 13 | 
             
                      "run-on" => { type: String,
         | 
| 12 14 | 
             
                                    desc: "The proxy target that the task executes on." }
         | 
| @@ -8,16 +8,25 @@ module Bolt | |
| 8 8 | 
             
                module Transport
         | 
| 9 9 | 
             
                  class SSH < Base
         | 
| 10 10 | 
             
                    LOGIN_SHELLS = %w[sh bash zsh dash ksh powershell].freeze
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    # NOTE: All transport configuration options should have a corresponding schema definition
         | 
| 13 | 
            +
                    #       in schemas/bolt-transport-definitions.json
         | 
| 11 14 | 
             
                    OPTIONS = {
         | 
| 12 15 | 
             
                      "cleanup"            => { type: TrueClass,
         | 
| 16 | 
            +
                                                external: true,
         | 
| 13 17 | 
             
                                                desc: "Whether to clean up temporary files created on targets." },
         | 
| 14 18 | 
             
                      "connect-timeout"    => { type: Integer,
         | 
| 15 19 | 
             
                                                desc: "How long to wait when establishing connections." },
         | 
| 20 | 
            +
                      "copy-command"       => { external: true,
         | 
| 21 | 
            +
                                                desc: "Command to use when copying files using ssh-command. "\
         | 
| 22 | 
            +
                                                      "Bolt runs `<copy-command> <src> <dest>`. **This option is experimental.**" },
         | 
| 16 23 | 
             
                      "disconnect-timeout" => { type: Integer,
         | 
| 17 24 | 
             
                                                desc: "How long to wait before force-closing a connection." },
         | 
| 18 25 | 
             
                      "host"               => { type: String,
         | 
| 26 | 
            +
                                                external: true,
         | 
| 19 27 | 
             
                                                desc: "Host name." },
         | 
| 20 28 | 
             
                      "host-key-check"     => { type: TrueClass,
         | 
| 29 | 
            +
                                                external: true,
         | 
| 21 30 | 
             
                                                desc: "Whether to perform host key validation when connecting." },
         | 
| 22 31 | 
             
                      "extensions"         => { type: Array,
         | 
| 23 32 | 
             
                                                desc: "List of file extensions that are accepted for scripts or tasks on Windows. "\
         | 
| @@ -26,6 +35,7 @@ module Bolt | |
| 26 35 | 
             
                                                     "a `.py` script runs with `python.exe`. The extensions `.ps1`, `.rb`, and "\
         | 
| 27 36 | 
             
                                                     "`.pp` are always allowed and run via hard-coded executables." },
         | 
| 28 37 | 
             
                      "interpreters"       => { type: Hash,
         | 
| 38 | 
            +
                                                external: true,
         | 
| 29 39 | 
             
                                                desc: "A map of an extension name to the absolute path of an executable, "\
         | 
| 30 40 | 
             
                                                      "enabling you to override the shebang defined in a task executable. The "\
         | 
| 31 41 | 
             
                                                      "extension can optionally be specified with the `.` character (`.py` and "\
         | 
| @@ -41,26 +51,36 @@ module Bolt | |
| 41 51 | 
             
                      "password"           => { type: String,
         | 
| 42 52 | 
             
                                                desc: "Login password." },
         | 
| 43 53 | 
             
                      "port"               => { type: Integer,
         | 
| 54 | 
            +
                                                external: true,
         | 
| 44 55 | 
             
                                                desc: "Connection port." },
         | 
| 45 | 
            -
                      "private-key"        => {  | 
| 56 | 
            +
                      "private-key"        => { external: true,
         | 
| 57 | 
            +
                                                desc: "Either the path to the private key file to use for authentication, or a "\
         | 
| 46 58 | 
             
                                                      "hash with the key `key-data` and the contents of the private key." },
         | 
| 47 59 | 
             
                      "proxyjump"          => { type: String,
         | 
| 48 60 | 
             
                                                desc: "A jump host to proxy connections through, and an optional user to "\
         | 
| 49 61 | 
             
                                                      "connect with." },
         | 
| 50 62 | 
             
                      "run-as"             => { type: String,
         | 
| 63 | 
            +
                                                external: true,
         | 
| 51 64 | 
             
                                                desc: "A different user to run commands as after login." },
         | 
| 52 65 | 
             
                      "run-as-command"     => { type: Array,
         | 
| 66 | 
            +
                                                external: true,
         | 
| 53 67 | 
             
                                                desc: "The command to elevate permissions. Bolt appends the user and command "\
         | 
| 54 68 | 
             
                                                      "strings to the configured `run-as-command` before running it on the "\
         | 
| 55 69 | 
             
                                                      "target. This command must not require an interactive password prompt, "\
         | 
| 56 70 | 
             
                                                      "and the `sudo-password` option is ignored when `run-as-command` is "\
         | 
| 57 71 | 
             
                                                      "specified. The `run-as-command` must be specified as an array." },
         | 
| 58 72 | 
             
                      "script-dir"         => { type: String,
         | 
| 73 | 
            +
                                                external: true,
         | 
| 59 74 | 
             
                                                desc: "The subdirectory of the tmpdir to use in place of a randomized "\
         | 
| 60 75 | 
             
                                                      "subdirectory for uploading and executing temporary files on the "\
         | 
| 61 76 | 
             
                                                      "target. It's expected that this directory already exists as a subdir "\
         | 
| 62 77 | 
             
                                                      "of tmpdir, which is either configured or defaults to `/tmp`." },
         | 
| 78 | 
            +
                      "ssh-command"        => { external: true,
         | 
| 79 | 
            +
                                                desc: "Command and flags to use when SSHing. This enables the external "\
         | 
| 80 | 
            +
                                                      "SSH transport which shells out to the specified command. "\
         | 
| 81 | 
            +
                                                      "**This option is experimental.**" },
         | 
| 63 82 | 
             
                      "sudo-executable"    => { type: String,
         | 
| 83 | 
            +
                                                external: true,
         | 
| 64 84 | 
             
                                                desc: "The executable to use when escalating to the configured `run-as` "\
         | 
| 65 85 | 
             
                                                      "user. This is useful when you want to escalate using the configured "\
         | 
| 66 86 | 
             
                                                      "`sudo-password`, since `run-as-command` does not use `sudo-password` "\
         | 
| @@ -68,14 +88,17 @@ module Bolt | |
| 68 88 | 
             
                                                      "`<sudo-executable> -S -u <user> -p custom_bolt_prompt <command>`. "\
         | 
| 69 89 | 
             
                                                      "**This option is experimental.**" },
         | 
| 70 90 | 
             
                      "sudo-password"      => { type: String,
         | 
| 91 | 
            +
                                                external: true,
         | 
| 71 92 | 
             
                                                desc: "Password to use when changing users via `run-as`." },
         | 
| 72 93 | 
             
                      "tmpdir"             => { type: String,
         | 
| 94 | 
            +
                                                external: true,
         | 
| 73 95 | 
             
                                                desc: "The directory to upload and execute temporary files on the target." },
         | 
| 74 96 | 
             
                      "tty"                => { type: TrueClass,
         | 
| 75 97 | 
             
                                                desc: "Request a pseudo tty for the session. This option is generally "\
         | 
| 76 98 | 
             
                                                      "only used in conjunction with the `run-as` option when the sudoers "\
         | 
| 77 99 | 
             
                                                      "policy requires a `tty`." },
         | 
| 78 100 | 
             
                      "user"               => { type: String,
         | 
| 101 | 
            +
                                                external: true,
         | 
| 79 102 | 
             
                                                desc: "Login user." }
         | 
| 80 103 | 
             
                    }.freeze
         | 
| 81 104 |  | 
| @@ -99,7 +122,14 @@ module Bolt | |
| 99 122 | 
             
                        end
         | 
| 100 123 |  | 
| 101 124 | 
             
                        if key_opt.instance_of?(String)
         | 
| 102 | 
            -
                          @config['private-key'] = File.expand_path(key_opt, @ | 
| 125 | 
            +
                          @config['private-key'] = File.expand_path(key_opt, @project)
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                          # We have an explicit test for this to only warn if using net-ssh transport
         | 
| 128 | 
            +
                          Bolt::Util.validate_file('ssh key', @config['private-key']) if @config['ssh-command']
         | 
| 129 | 
            +
                        end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                        if key_opt.instance_of?(Hash) && @config['ssh-command']
         | 
| 132 | 
            +
                          raise Bolt::ValidationError, 'private-key must be a filepath when using ssh-command'
         | 
| 103 133 | 
             
                        end
         | 
| 104 134 | 
             
                      end
         | 
| 105 135 |  | 
| @@ -127,6 +157,25 @@ module Bolt | |
| 127 157 | 
             
                          end
         | 
| 128 158 | 
             
                        end
         | 
| 129 159 | 
             
                      end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                      if @config['ssh-command'] && !@config['load-config']
         | 
| 162 | 
            +
                        msg = 'Cannot use external SSH transport with load-config set to false'
         | 
| 163 | 
            +
                        raise Bolt::ValidationError, msg
         | 
| 164 | 
            +
                      end
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                      if (ssh_cmd = @config['ssh-command'])
         | 
| 167 | 
            +
                        unless ssh_cmd.is_a?(String) || ssh_cmd.is_a?(Array)
         | 
| 168 | 
            +
                          raise Bolt::ValidationError,
         | 
| 169 | 
            +
                                "ssh-command must be a String or Array, received #{ssh_cmd.class} #{ssh_cmd.inspect}"
         | 
| 170 | 
            +
                        end
         | 
| 171 | 
            +
                      end
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                      if (copy_cmd = @config['copy-command'])
         | 
| 174 | 
            +
                        unless copy_cmd.is_a?(String) || copy_cmd.is_a?(Array)
         | 
| 175 | 
            +
                          raise Bolt::ValidationError,
         | 
| 176 | 
            +
                                "copy-command must be a String or Array, received #{copy_cmd.class} #{copy_cmd.inspect}"
         | 
| 177 | 
            +
                        end
         | 
| 178 | 
            +
                      end
         | 
| 130 179 | 
             
                    end
         | 
| 131 180 | 
             
                  end
         | 
| 132 181 | 
             
                end
         | 
| @@ -7,6 +7,8 @@ module Bolt | |
| 7 7 | 
             
              class Config
         | 
| 8 8 | 
             
                module Transport
         | 
| 9 9 | 
             
                  class WinRM < Base
         | 
| 10 | 
            +
                    # NOTE: All transport configuration options should have a corresponding schema definition
         | 
| 11 | 
            +
                    #       in schemas/bolt-transport-definitions.json
         | 
| 10 12 | 
             
                    OPTIONS = {
         | 
| 11 13 | 
             
                      "basic-auth-only" => { type: TrueClass,
         | 
| 12 14 | 
             
                                             desc: "Force basic authentication. This option is only available when using SSL." },
         | 
| @@ -71,7 +73,7 @@ module Bolt | |
| 71 73 | 
             
                        end
         | 
| 72 74 |  | 
| 73 75 | 
             
                        if @config['cacert']
         | 
| 74 | 
            -
                          @config['cacert'] = File.expand_path(@config['cacert'], @ | 
| 76 | 
            +
                          @config['cacert'] = File.expand_path(@config['cacert'], @project)
         | 
| 75 77 | 
             
                          Bolt::Util.validate_file('cacert', @config['cacert'])
         | 
| 76 78 | 
             
                        end
         | 
| 77 79 | 
             
                      end
         | 
    
        data/lib/bolt/executor.rb
    CHANGED
    
    | @@ -278,6 +278,22 @@ module Bolt | |
| 278 278 | 
             
                  end
         | 
| 279 279 | 
             
                end
         | 
| 280 280 |  | 
| 281 | 
            +
                def run_task_with(target_mapping, task, options = {})
         | 
| 282 | 
            +
                  targets = target_mapping.keys
         | 
| 283 | 
            +
                  description = options.fetch(:description, "task #{task.name}")
         | 
| 284 | 
            +
             | 
| 285 | 
            +
                  log_action(description, targets) do
         | 
| 286 | 
            +
                    options[:run_as] = run_as if run_as && !options.key?(:run_as)
         | 
| 287 | 
            +
                    target_mapping.each_value { |arguments| arguments['_task'] = task.name }
         | 
| 288 | 
            +
             | 
| 289 | 
            +
                    batch_execute(targets) do |transport, batch|
         | 
| 290 | 
            +
                      with_node_logging("Running task #{task.name}'", batch) do
         | 
| 291 | 
            +
                        transport.batch_task_with(batch, task, target_mapping, options, &method(:publish_event))
         | 
| 292 | 
            +
                      end
         | 
| 293 | 
            +
                    end
         | 
| 294 | 
            +
                  end
         | 
| 295 | 
            +
                end
         | 
| 296 | 
            +
             | 
| 281 297 | 
             
                def upload_file(targets, source, destination, options = {})
         | 
| 282 298 | 
             
                  description = options.fetch(:description, "file upload from #{source} to #{destination}")
         | 
| 283 299 | 
             
                  log_action(description, targets) do
         | 
    
        data/lib/bolt/inventory.rb
    CHANGED
    
    | @@ -15,6 +15,7 @@ module Bolt | |
| 15 15 |  | 
| 16 16 | 
             
                class ValidationError < Bolt::Error
         | 
| 17 17 | 
             
                  attr_accessor :path
         | 
| 18 | 
            +
             | 
| 18 19 | 
             
                  def initialize(message, offending_group)
         | 
| 19 20 | 
             
                    super(message, 'bolt.inventory/validation-error')
         | 
| 20 21 | 
             
                    @_message = message
         | 
| @@ -83,7 +84,7 @@ module Bolt | |
| 83 84 |  | 
| 84 85 | 
             
                def self.empty
         | 
| 85 86 | 
             
                  config  = Bolt::Config.default
         | 
| 86 | 
            -
                  plugins = Bolt::Plugin.setup(config, nil | 
| 87 | 
            +
                  plugins = Bolt::Plugin.setup(config, nil)
         | 
| 87 88 |  | 
| 88 89 | 
             
                  create_version({}, config.transport, config.transports, plugins)
         | 
| 89 90 | 
             
                end
         | 
    
        data/lib/bolt/inventory/group.rb
    CHANGED
    
    | @@ -12,6 +12,7 @@ module Bolt | |
| 12 12 | 
             
                  # Regex used to validate group names and target aliases.
         | 
| 13 13 | 
             
                  NAME_REGEX = /\A[a-z0-9_][a-z0-9_-]*\Z/.freeze
         | 
| 14 14 |  | 
| 15 | 
            +
                  # NOTE: All keys should have a corresponding schema property in schemas/bolt-inventory.schema.json
         | 
| 15 16 | 
             
                  DATA_KEYS = %w[config facts vars features plugin_hooks].freeze
         | 
| 16 17 | 
             
                  TARGET_KEYS = DATA_KEYS + %w[name alias uri]
         | 
| 17 18 | 
             
                  GROUP_KEYS = DATA_KEYS + %w[name groups targets]
         | 
| @@ -7,6 +7,7 @@ module Bolt | |
| 7 7 | 
             
              class Inventory
         | 
| 8 8 | 
             
                class Inventory
         | 
| 9 9 | 
             
                  attr_reader :targets, :plugins, :config, :transport
         | 
| 10 | 
            +
             | 
| 10 11 | 
             
                  class WildcardError < Bolt::Error
         | 
| 11 12 | 
             
                    def initialize(target)
         | 
| 12 13 | 
             
                      super("Found 0 targets matching wildcard pattern #{target}", 'bolt.inventory/wildcard-error')
         | 
| @@ -318,6 +319,10 @@ module Bolt | |
| 318 319 | 
             
                  def target_config(target)
         | 
| 319 320 | 
             
                    @targets[target.name].config
         | 
| 320 321 | 
             
                  end
         | 
| 322 | 
            +
             | 
| 323 | 
            +
                  def resources(target)
         | 
| 324 | 
            +
                    @targets[target.name].resources
         | 
| 325 | 
            +
                  end
         | 
| 321 326 | 
             
                end
         | 
| 322 327 | 
             
              end
         | 
| 323 328 | 
             
            end
         | 
| @@ -1,10 +1,12 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require 'bolt/resource_instance'
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module Bolt
         | 
| 4 6 | 
             
              class Inventory
         | 
| 5 7 | 
             
                # This class represents the active state of a target within the inventory.
         | 
| 6 8 | 
             
                class Target
         | 
| 7 | 
            -
                  attr_reader :name, :uri, :safe_name, :target_alias
         | 
| 9 | 
            +
                  attr_reader :name, :uri, :safe_name, :target_alias, :resources
         | 
| 8 10 |  | 
| 9 11 | 
             
                  def initialize(target_data, inventory)
         | 
| 10 12 | 
             
                    unless target_data['name'] || target_data['uri']
         | 
| @@ -36,12 +38,26 @@ module Bolt | |
| 36 38 | 
             
                    # When alias is specified in a plan, the key will be `target_alias`, when
         | 
| 37 39 | 
             
                    # alias is specified in inventory the key will be `alias`.
         | 
| 38 40 | 
             
                    @target_alias = target_data['target_alias'] || target_data['alias'] || []
         | 
| 41 | 
            +
                    @resources = {}
         | 
| 39 42 |  | 
| 40 43 | 
             
                    @inventory = inventory
         | 
| 41 44 |  | 
| 42 45 | 
             
                    validate
         | 
| 43 46 | 
             
                  end
         | 
| 44 47 |  | 
| 48 | 
            +
                  # rubocop:disable Naming/AccessorMethodName
         | 
| 49 | 
            +
                  def set_resource(resource)
         | 
| 50 | 
            +
                    if (existing_resource = resources[resource.reference])
         | 
| 51 | 
            +
                      existing_resource.overwrite_state(resource.state)
         | 
| 52 | 
            +
                      existing_resource.overwrite_desired_state(resource.desired_state)
         | 
| 53 | 
            +
                      existing_resource.events = existing_resource.events + resource.events
         | 
| 54 | 
            +
                      existing_resource
         | 
| 55 | 
            +
                    else
         | 
| 56 | 
            +
                      @resources[resource.reference] = resource
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                  # rubocop:enable Naming/AccessorMethodName
         | 
| 60 | 
            +
             | 
| 45 61 | 
             
                  def vars
         | 
| 46 62 | 
             
                    group_cache['vars'].merge(@vars)
         | 
| 47 63 | 
             
                  end
         | 
    
        data/lib/bolt/node/output.rb
    CHANGED
    
    
    
        data/lib/bolt/outputter/human.rb
    CHANGED
    
    | @@ -107,13 +107,14 @@ module Bolt | |
| 107 107 |  | 
| 108 108 | 
             
                      # Use special handling if the result looks like a command or script result
         | 
| 109 109 | 
             
                      if result.generic_value.keys == %w[stdout stderr exit_code]
         | 
| 110 | 
            -
                         | 
| 110 | 
            +
                        safe_value = result.safe_value
         | 
| 111 | 
            +
                        unless safe_value['stdout'].strip.empty?
         | 
| 111 112 | 
             
                          @stream.puts(indent(2, "STDOUT:"))
         | 
| 112 | 
            -
                          @stream.puts(indent(4,  | 
| 113 | 
            +
                          @stream.puts(indent(4, safe_value['stdout']))
         | 
| 113 114 | 
             
                        end
         | 
| 114 | 
            -
                        unless  | 
| 115 | 
            +
                        unless safe_value['stderr'].strip.empty?
         | 
| 115 116 | 
             
                          @stream.puts(indent(2, "STDERR:"))
         | 
| 116 | 
            -
                          @stream.puts(indent(4,  | 
| 117 | 
            +
                          @stream.puts(indent(4, safe_value['stderr']))
         | 
| 117 118 | 
             
                        end
         | 
| 118 119 | 
             
                      elsif result.generic_value.any?
         | 
| 119 120 | 
             
                        @stream.puts(indent(2, ::JSON.pretty_generate(result.generic_value)))
         | 
    
        data/lib/bolt/outputter/json.rb
    CHANGED
    
    
    
        data/lib/bolt/pal.rb
    CHANGED
    
    | @@ -40,7 +40,7 @@ module Bolt | |
| 40 40 | 
             
                attr_reader :modulepath
         | 
| 41 41 |  | 
| 42 42 | 
             
                def initialize(modulepath, hiera_config, resource_types, max_compiles = Etc.nprocessors,
         | 
| 43 | 
            -
                               trusted_external = nil, apply_settings = {})
         | 
| 43 | 
            +
                               trusted_external = nil, apply_settings = {}, project = nil)
         | 
| 44 44 | 
             
                  # Nothing works without initialized this global state. Reinitializing
         | 
| 45 45 | 
             
                  # is safe and in practice only happens in tests
         | 
| 46 46 | 
             
                  self.class.load_puppet
         | 
| @@ -52,6 +52,7 @@ module Bolt | |
| 52 52 | 
             
                  @apply_settings = apply_settings
         | 
| 53 53 | 
             
                  @max_compiles = max_compiles
         | 
| 54 54 | 
             
                  @resource_types = resource_types
         | 
| 55 | 
            +
                  @project = project
         | 
| 55 56 |  | 
| 56 57 | 
             
                  @logger = Logging.logger[self]
         | 
| 57 58 | 
             
                  if modulepath && !modulepath.empty?
         | 
| @@ -114,7 +115,7 @@ module Bolt | |
| 114 115 | 
             
                  compiler.evaluate_string('type PlanResult = Boltlib::PlanResult')
         | 
| 115 116 | 
             
                end
         | 
| 116 117 |  | 
| 117 | 
            -
                # Register all resource types defined in $ | 
| 118 | 
            +
                # Register all resource types defined in $Project/.resource_types as well as
         | 
| 118 119 | 
             
                # the built in types registered with the runtime_3_init method.
         | 
| 119 120 | 
             
                def register_resource_types(loaders)
         | 
| 120 121 | 
             
                  static_loader = loaders.static_loader
         | 
| @@ -136,19 +137,34 @@ module Bolt | |
| 136 137 | 
             
                  # TODO: If we always call this inside a bolt_executor we can remove this here
         | 
| 137 138 | 
             
                  setup
         | 
| 138 139 | 
             
                  r = Puppet::Pal.in_tmp_environment('bolt', modulepath: @modulepath, facts: {}) do |pal|
         | 
| 139 | 
            -
                     | 
| 140 | 
            -
             | 
| 141 | 
            -
                       | 
| 142 | 
            -
             | 
| 143 | 
            -
                        Puppet. | 
| 140 | 
            +
                    Puppet.override(bolt_project: @project,
         | 
| 141 | 
            +
                                    yaml_plan_instantiator: Bolt::PAL::YamlPlan::Loader) do
         | 
| 142 | 
            +
                      pal.with_script_compiler do |compiler|
         | 
| 143 | 
            +
                        alias_types(compiler)
         | 
| 144 | 
            +
                        register_resource_types(Puppet.lookup(:loaders)) if @resource_types
         | 
| 145 | 
            +
                        begin
         | 
| 144 146 | 
             
                          yield compiler
         | 
| 147 | 
            +
                        rescue Bolt::Error => e
         | 
| 148 | 
            +
                          e
         | 
| 149 | 
            +
                        rescue Puppet::DataBinding::LookupError => e
         | 
| 150 | 
            +
                          if e.issue_code == :HIERA_UNDEFINED_VARIABLE
         | 
| 151 | 
            +
                            message = "Interpolations are not supported in lookups outside of an apply block: #{e.message}"
         | 
| 152 | 
            +
                            PALError.new(message)
         | 
| 153 | 
            +
                          else
         | 
| 154 | 
            +
                            PALError.from_preformatted_error(e)
         | 
| 155 | 
            +
                          end
         | 
| 156 | 
            +
                        rescue Puppet::PreformattedError => e
         | 
| 157 | 
            +
                          if e.issue_code == :UNKNOWN_VARIABLE &&
         | 
| 158 | 
            +
                             %w[facts trusted server_facts settings].include?(e.arguments[:name])
         | 
| 159 | 
            +
                            message = "Evaluation Error: Variable '#{e.arguments[:name]}' is not available in the current scope "\
         | 
| 160 | 
            +
                              "unless explicitly defined. (file: #{e.file}, line: #{e.line}, column: #{e.pos})"
         | 
| 161 | 
            +
                            PALError.new(message)
         | 
| 162 | 
            +
                          else
         | 
| 163 | 
            +
                            PALError.from_preformatted_error(e)
         | 
| 164 | 
            +
                          end
         | 
| 165 | 
            +
                        rescue StandardError => e
         | 
| 166 | 
            +
                          PALError.from_preformatted_error(e)
         | 
| 145 167 | 
             
                        end
         | 
| 146 | 
            -
                      rescue Bolt::Error => e
         | 
| 147 | 
            -
                        e
         | 
| 148 | 
            -
                      rescue Puppet::PreformattedError => e
         | 
| 149 | 
            -
                        PALError.from_preformatted_error(e)
         | 
| 150 | 
            -
                      rescue StandardError => e
         | 
| 151 | 
            -
                        PALError.from_preformatted_error(e)
         | 
| 152 168 | 
             
                      end
         | 
| 153 169 | 
             
                    end
         | 
| 154 170 | 
             
                  end
         | 
| @@ -215,6 +231,7 @@ module Bolt | |
| 215 231 | 
             
                    Puppet.initialize_settings(cli)
         | 
| 216 232 | 
             
                    Puppet::GettextConfig.create_default_text_domain
         | 
| 217 233 | 
             
                    Puppet[:trusted_external_command] = @trusted_external
         | 
| 234 | 
            +
                    Puppet.settings[:hiera_config] = @hiera_config
         | 
| 218 235 | 
             
                    self.class.configure_logging
         | 
| 219 236 | 
             
                    yield
         | 
| 220 237 | 
             
                  end
         | 
| @@ -312,7 +329,8 @@ module Bolt | |
| 312 329 | 
             
                    raise Bolt::Error.unknown_plan(plan_name)
         | 
| 313 330 | 
             
                  end
         | 
| 314 331 |  | 
| 315 | 
            -
                   | 
| 332 | 
            +
                  # path may be a Pathname object, so make sure to stringify it
         | 
| 333 | 
            +
                  mod = plan_sig.instance_variable_get(:@plan_func).loader.parent.path.to_s
         | 
| 316 334 |  | 
| 317 335 | 
             
                  # If it's a Puppet language plan, use strings to extract data. The only
         | 
| 318 336 | 
             
                  # way to tell is to check which filename exists in the module.
         |