bolt 1.12.0 → 1.13.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/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +4 -3
- data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +7 -0
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +8 -0
- data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +7 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +7 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +6 -6
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +4 -3
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +6 -6
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +5 -4
- data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +5 -4
- data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +4 -3
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +6 -6
- data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +6 -5
- data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +8 -0
- data/lib/bolt/config.rb +14 -26
- data/lib/bolt/pal.rb +1 -0
- data/lib/bolt/pal/issues.rb +13 -0
- data/lib/bolt/transport/base.rb +8 -0
- data/lib/bolt/transport/docker.rb +6 -6
- data/lib/bolt/transport/docker/connection.rb +7 -4
- data/lib/bolt/transport/local.rb +25 -10
- data/lib/bolt/transport/local/shell.rb +1 -0
- data/lib/bolt/transport/orch.rb +4 -0
- data/lib/bolt/transport/remote.rb +4 -0
- data/lib/bolt/transport/ssh.rb +10 -1
- data/lib/bolt/transport/ssh/connection.rb +4 -0
- data/lib/bolt/transport/winrm.rb +24 -2
- data/lib/bolt/transport/winrm/connection.rb +112 -2
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/schemas/ssh-run_task.json +4 -0
- data/lib/bolt_server/schemas/winrm-run_task.json +14 -0
- data/lib/bolt_spec/plans.rb +5 -0
- metadata +17 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 66234162abaa83dd3dd590b21dd87a54d10c3da44f0d50ccd8e28d82ddf76178
         | 
| 4 | 
            +
              data.tar.gz: b056cb53e8fda46db48b7a7a1b63f8441f80342e11e5c0a7c50d1813cf852bbc
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: '079515ce4801e6913e2b75d276289985cf9eda0d9b60c10021a5b435c77ce69d0cc07d5a1708a4f7c43db4947fef918f5d14b8ccac8fdf6a55b5ccf5fce6cf62'
         | 
| 7 | 
            +
              data.tar.gz: 94eba8ff4a83d463f4b5516eb69cd83d31b1fdc6c3673a0b0beda9ac5dc08becd9eb2d75953932c57345ab8099900884174df329d04f43637802df73a538a365
         | 
| @@ -3,6 +3,8 @@ | |
| 3 3 | 
             
            require 'bolt/error'
         | 
| 4 4 |  | 
| 5 5 | 
             
            # Deep merges a hash of facts with the existing facts on a target.
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            # **NOTE:** Not available in apply block
         | 
| 6 8 | 
             
            Puppet::Functions.create_function(:add_facts) do
         | 
| 7 9 | 
             
              # @param target A target.
         | 
| 8 10 | 
             
              # @param facts A hash of fact names to values that may include structured facts.
         | 
| @@ -17,9 +19,8 @@ Puppet::Functions.create_function(:add_facts) do | |
| 17 19 |  | 
| 18 20 | 
             
              def add_facts(target, facts)
         | 
| 19 21 | 
             
                unless Puppet[:tasks]
         | 
| 20 | 
            -
                  raise Puppet::ParseErrorWithIssue | 
| 21 | 
            -
                     | 
| 22 | 
            -
                  )
         | 
| 22 | 
            +
                  raise Puppet::ParseErrorWithIssue
         | 
| 23 | 
            +
                    .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'add_facts')
         | 
| 23 24 | 
             
                end
         | 
| 24 25 |  | 
| 25 26 | 
             
                inventory = Puppet.lookup(:bolt_inventory) { nil }
         | 
| @@ -3,6 +3,8 @@ | |
| 3 3 | 
             
            require 'bolt/error'
         | 
| 4 4 |  | 
| 5 5 | 
             
            # Adds a target to specified inventory group.
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            # **NOTE:** Not available in apply block
         | 
| 6 8 | 
             
            Puppet::Functions.create_function(:add_to_group) do
         | 
| 7 9 | 
             
              # @param targets A pattern or array of patterns identifying a set of targets.
         | 
| 8 10 | 
             
              # @param group The name of the group to add targets to.
         | 
| @@ -20,6 +22,11 @@ Puppet::Functions.create_function(:add_to_group) do | |
| 20 22 | 
             
              end
         | 
| 21 23 |  | 
| 22 24 | 
             
              def add_to_group(targets, group)
         | 
| 25 | 
            +
                unless Puppet[:tasks]
         | 
| 26 | 
            +
                  raise Puppet::ParseErrorWithIssue
         | 
| 27 | 
            +
                    .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'add_to_group')
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 23 30 | 
             
                inventory = Puppet.lookup(:bolt_inventory) { nil }
         | 
| 24 31 |  | 
| 25 32 | 
             
                unless inventory && Puppet.features.bolt?
         | 
| @@ -10,6 +10,8 @@ require 'bolt/task' | |
| 10 10 | 
             
            #
         | 
| 11 11 | 
             
            # If no agent is detected on the target using the 'puppet_agent::version' task, it's installed
         | 
| 12 12 | 
             
            # using 'puppet_agent::install' and the puppet service is stopped/disabled using the 'service' task.
         | 
| 13 | 
            +
            #
         | 
| 14 | 
            +
            # **NOTE:** Not available in apply block
         | 
| 13 15 | 
             
            Puppet::Functions.create_function(:apply_prep) do
         | 
| 14 16 | 
             
              # @param targets A pattern or array of patterns identifying a set of targets.
         | 
| 15 17 | 
             
              # @example Prepare targets by name.
         | 
| @@ -39,9 +41,15 @@ Puppet::Functions.create_function(:apply_prep) do | |
| 39 41 | 
             
              end
         | 
| 40 42 |  | 
| 41 43 | 
             
              def apply_prep(target_spec)
         | 
| 44 | 
            +
                unless Puppet[:tasks]
         | 
| 45 | 
            +
                  raise Puppet::ParseErrorWithIssue
         | 
| 46 | 
            +
                    .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'apply_prep')
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 42 49 | 
             
                applicator = Puppet.lookup(:apply_executor) { nil }
         | 
| 43 50 | 
             
                executor = Puppet.lookup(:bolt_executor) { nil }
         | 
| 44 51 | 
             
                inventory = Puppet.lookup(:bolt_inventory) { nil }
         | 
| 52 | 
            +
             | 
| 45 53 | 
             
                unless applicator && executor && inventory && Puppet.features.bolt?
         | 
| 46 54 | 
             
                  raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
         | 
| 47 55 | 
             
                    Puppet::Pops::Issues::TASK_MISSING_BOLT, action: _('apply_prep')
         | 
| @@ -6,6 +6,8 @@ require 'bolt/error' | |
| 6 6 | 
             
            #
         | 
| 7 7 | 
             
            # Plan authors should call this function when their plan is not successful. The
         | 
| 8 8 | 
             
            # error may then be caught by another plans run_plan function or in bolt itself
         | 
| 9 | 
            +
            #
         | 
| 10 | 
            +
            # **NOTE:** Not available in apply block
         | 
| 9 11 | 
             
            Puppet::Functions.create_function(:fail_plan) do
         | 
| 10 12 | 
             
              # Fail a plan, generating an exception from the parameters.
         | 
| 11 13 | 
             
              # @param msg An error message.
         | 
| @@ -32,6 +34,11 @@ Puppet::Functions.create_function(:fail_plan) do | |
| 32 34 | 
             
              end
         | 
| 33 35 |  | 
| 34 36 | 
             
              def from_args(msg, kind = nil, details = nil, issue_code = nil)
         | 
| 37 | 
            +
                unless Puppet[:tasks]
         | 
| 38 | 
            +
                  raise Puppet::ParseErrorWithIssue
         | 
| 39 | 
            +
                    .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'fail_plan')
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 35 42 | 
             
                executor = Puppet.lookup(:bolt_executor) { nil }
         | 
| 36 43 | 
             
                executor&.report_function_call('fail_plan')
         | 
| 37 44 |  | 
| @@ -7,6 +7,8 @@ require 'bolt/task' | |
| 7 7 | 
             
            #
         | 
| 8 8 | 
             
            # Requires the Puppet Agent be installed on the target, which can be accomplished with apply_prep
         | 
| 9 9 | 
             
            # or by directly running the puppet_agent::install task.
         | 
| 10 | 
            +
            #
         | 
| 11 | 
            +
            # **NOTE:** Not available in apply block
         | 
| 10 12 | 
             
            Puppet::Functions.create_function(:get_resources) do
         | 
| 11 13 | 
             
              # @param targets A pattern or array of patterns identifying a set of targets.
         | 
| 12 14 | 
             
              # @param resources A resource type or instance, or an array of such.
         | 
| @@ -32,6 +34,11 @@ Puppet::Functions.create_function(:get_resources) do | |
| 32 34 | 
             
              end
         | 
| 33 35 |  | 
| 34 36 | 
             
              def get_resources(target_spec, resources)
         | 
| 37 | 
            +
                unless Puppet[:tasks]
         | 
| 38 | 
            +
                  raise Puppet::ParseErrorWithIssue
         | 
| 39 | 
            +
                    .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'get_resources')
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 35 42 | 
             
                applicator = Puppet.lookup(:apply_executor) { nil }
         | 
| 36 43 | 
             
                executor = Puppet.lookup(:bolt_executor) { nil }
         | 
| 37 44 | 
             
                inventory = Puppet.lookup(:bolt_inventory) { nil }
         | 
| @@ -4,6 +4,8 @@ require 'bolt/error' | |
| 4 4 |  | 
| 5 5 | 
             
            # Runs a command on the given set of targets and returns the result from each command execution.
         | 
| 6 6 | 
             
            # This function does nothing if the list of targets is empty.
         | 
| 7 | 
            +
            #
         | 
| 8 | 
            +
            # **NOTE:** Not available in apply block
         | 
| 7 9 | 
             
            Puppet::Functions.create_function(:run_command) do
         | 
| 8 10 | 
             
              # Run a command.
         | 
| 9 11 | 
             
              # @param command A command to run on target.
         | 
| @@ -40,15 +42,13 @@ Puppet::Functions.create_function(:run_command) do | |
| 40 42 | 
             
              end
         | 
| 41 43 |  | 
| 42 44 | 
             
              def run_command_with_description(command, targets, description = nil, options = nil)
         | 
| 43 | 
            -
                options ||= {}
         | 
| 44 | 
            -
                options = options.merge('_description' => description) if description
         | 
| 45 | 
            -
             | 
| 46 45 | 
             
                unless Puppet[:tasks]
         | 
| 47 | 
            -
                  raise Puppet::ParseErrorWithIssue | 
| 48 | 
            -
                     | 
| 49 | 
            -
                  )
         | 
| 46 | 
            +
                  raise Puppet::ParseErrorWithIssue
         | 
| 47 | 
            +
                    .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'run_command')
         | 
| 50 48 | 
             
                end
         | 
| 51 49 |  | 
| 50 | 
            +
                options ||= {}
         | 
| 51 | 
            +
                options = options.merge('_description' => description) if description
         | 
| 52 52 | 
             
                executor = Puppet.lookup(:bolt_executor) { nil }
         | 
| 53 53 | 
             
                inventory = Puppet.lookup(:bolt_inventory) { nil }
         | 
| 54 54 | 
             
                unless executor && inventory && Puppet.features.bolt?
         | 
| @@ -3,6 +3,8 @@ | |
| 3 3 | 
             
            require 'bolt/error'
         | 
| 4 4 |  | 
| 5 5 | 
             
            # Runs the `plan` referenced by its name. A plan is autoloaded from `<moduleroot>/plans`.
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            # **NOTE:** Not available in apply block
         | 
| 6 8 | 
             
            Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction) do
         | 
| 7 9 | 
             
              # @param plan_name The plan to run.
         | 
| 8 10 | 
             
              # @param named_args Arguments to the plan. Can also include additional options: '_catch_errors', '_run_as'.
         | 
| @@ -18,9 +20,8 @@ Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction | |
| 18 20 |  | 
| 19 21 | 
             
              def run_plan(scope, plan_name, named_args = {})
         | 
| 20 22 | 
             
                unless Puppet[:tasks]
         | 
| 21 | 
            -
                  raise Puppet::ParseErrorWithIssue | 
| 22 | 
            -
                     | 
| 23 | 
            -
                  )
         | 
| 23 | 
            +
                  raise Puppet::ParseErrorWithIssue
         | 
| 24 | 
            +
                    .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'run_plan')
         | 
| 24 25 | 
             
                end
         | 
| 25 26 |  | 
| 26 27 | 
             
                executor = Puppet.lookup(:bolt_executor) { nil }
         | 
| @@ -2,6 +2,8 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            # Uploads the given script to the given set of targets and returns the result of having each target execute the script.
         | 
| 4 4 | 
             
            # This function does nothing if the list of targets is empty.
         | 
| 5 | 
            +
            #
         | 
| 6 | 
            +
            # **NOTE:** Not available in apply block
         | 
| 5 7 | 
             
            Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFunction) do
         | 
| 6 8 | 
             
              # Run a script.
         | 
| 7 9 | 
             
              # @param script Path to a script to run on target. May be an absolute path or a modulename/filename selector for a
         | 
| @@ -46,15 +48,13 @@ Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFuncti | |
| 46 48 | 
             
              end
         | 
| 47 49 |  | 
| 48 50 | 
             
              def run_script_with_description(scope, script, targets, description = nil, options = nil)
         | 
| 49 | 
            -
                options ||= {}
         | 
| 50 | 
            -
                options = options.merge('_description' => description) if description
         | 
| 51 | 
            -
             | 
| 52 51 | 
             
                unless Puppet[:tasks]
         | 
| 53 | 
            -
                  raise Puppet::ParseErrorWithIssue | 
| 54 | 
            -
                     | 
| 55 | 
            -
                  )
         | 
| 52 | 
            +
                  raise Puppet::ParseErrorWithIssue
         | 
| 53 | 
            +
                    .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'run_script')
         | 
| 56 54 | 
             
                end
         | 
| 57 55 |  | 
| 56 | 
            +
                options ||= {}
         | 
| 57 | 
            +
                options = options.merge('_description' => description) if description
         | 
| 58 58 | 
             
                executor = Puppet.lookup(:bolt_executor) { nil }
         | 
| 59 59 | 
             
                inventory = Puppet.lookup(:bolt_inventory) { nil }
         | 
| 60 60 | 
             
                unless executor && inventory && Puppet.features.bolt?
         | 
| @@ -6,6 +6,8 @@ require 'bolt/task' | |
| 6 6 |  | 
| 7 7 | 
             
            # Runs a given instance of a `Task` on the given set of targets and returns the result from each.
         | 
| 8 8 | 
             
            # This function does nothing if the list of targets is empty.
         | 
| 9 | 
            +
            #
         | 
| 10 | 
            +
            # **NOTE:** Not available in apply block
         | 
| 9 11 | 
             
            Puppet::Functions.create_function(:run_task) do
         | 
| 10 12 | 
             
              # Run a task.
         | 
| 11 13 | 
             
              # @param task_name The task to run.
         | 
| @@ -67,13 +69,12 @@ Puppet::Functions.create_function(:run_task) do | |
| 67 69 | 
             
              end
         | 
| 68 70 |  | 
| 69 71 | 
             
              def run_task_raw(task_name, targets, description = nil, task_args = nil, &block)
         | 
| 70 | 
            -
                task_args ||= {}
         | 
| 71 72 | 
             
                unless Puppet[:tasks]
         | 
| 72 | 
            -
                  raise Puppet::ParseErrorWithIssue | 
| 73 | 
            -
                     | 
| 74 | 
            -
                  )
         | 
| 73 | 
            +
                  raise Puppet::ParseErrorWithIssue
         | 
| 74 | 
            +
                    .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'run_task')
         | 
| 75 75 | 
             
                end
         | 
| 76 76 |  | 
| 77 | 
            +
                task_args ||= {}
         | 
| 77 78 | 
             
                executor = Puppet.lookup(:bolt_executor) { nil }
         | 
| 78 79 | 
             
                inventory = Puppet.lookup(:bolt_inventory) { nil }
         | 
| 79 80 | 
             
                unless executor && inventory && Puppet.features.bolt?
         | 
| @@ -9,11 +9,13 @@ require 'bolt/error' | |
| 9 9 | 
             
            # - powershell
         | 
| 10 10 | 
             
            # - shell
         | 
| 11 11 | 
             
            # - puppet-agent
         | 
| 12 | 
            +
            #
         | 
| 13 | 
            +
            # **NOTE:** Not available in apply block
         | 
| 12 14 | 
             
            Puppet::Functions.create_function(:set_feature) do
         | 
| 13 15 | 
             
              # @param target The Target object to add features to. See {get_targets}.
         | 
| 14 16 | 
             
              # @param feature The string identifying the feature.
         | 
| 15 17 | 
             
              # @param value Whether the feature is supported.
         | 
| 16 | 
            -
              # @return  | 
| 18 | 
            +
              # @return The target with the updated feature
         | 
| 17 19 | 
             
              # @example Add the puppet-agent feature to a target
         | 
| 18 20 | 
             
              #   set_feature($target, 'puppet-agent', true)
         | 
| 19 21 | 
             
              dispatch :set_feature do
         | 
| @@ -24,9 +26,8 @@ Puppet::Functions.create_function(:set_feature) do | |
| 24 26 |  | 
| 25 27 | 
             
              def set_feature(target, feature, value = true)
         | 
| 26 28 | 
             
                unless Puppet[:tasks]
         | 
| 27 | 
            -
                  raise Puppet::ParseErrorWithIssue | 
| 28 | 
            -
                     | 
| 29 | 
            -
                  )
         | 
| 29 | 
            +
                  raise Puppet::ParseErrorWithIssue
         | 
| 30 | 
            +
                    .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'set_feature')
         | 
| 30 31 | 
             
                end
         | 
| 31 32 |  | 
| 32 33 | 
             
                inventory = Puppet.lookup(:bolt_inventory) { nil }
         | 
| @@ -3,6 +3,8 @@ | |
| 3 3 | 
             
            require 'bolt/error'
         | 
| 4 4 |  | 
| 5 5 | 
             
            # Sets a variable { key => value } for a target.
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            # **NOTE:** Not available in apply block
         | 
| 6 8 | 
             
            Puppet::Functions.create_function(:set_var) do
         | 
| 7 9 | 
             
              # @param target The Target object to set the variable for. See {get_targets}.
         | 
| 8 10 | 
             
              # @param key The key for the variable.
         | 
| @@ -18,9 +20,8 @@ Puppet::Functions.create_function(:set_var) do | |
| 18 20 |  | 
| 19 21 | 
             
              def set_var(target, key, value)
         | 
| 20 22 | 
             
                unless Puppet[:tasks]
         | 
| 21 | 
            -
                  raise Puppet::ParseErrorWithIssue | 
| 22 | 
            -
                     | 
| 23 | 
            -
                  )
         | 
| 23 | 
            +
                  raise Puppet::ParseErrorWithIssue
         | 
| 24 | 
            +
                    .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'set_var')
         | 
| 24 25 | 
             
                end
         | 
| 25 26 |  | 
| 26 27 | 
             
                inventory = Puppet.lookup(:bolt_inventory) { nil }
         | 
| @@ -4,6 +4,8 @@ require 'bolt/error' | |
| 4 4 |  | 
| 5 5 | 
             
            # Uploads the given file or directory to the given set of targets and returns the result from each upload.
         | 
| 6 6 | 
             
            # This function does nothing if the list of targets is empty.
         | 
| 7 | 
            +
            #
         | 
| 8 | 
            +
            # **NOTE:** Not available in apply block
         | 
| 7 9 | 
             
            Puppet::Functions.create_function(:upload_file, Puppet::Functions::InternalFunction) do
         | 
| 8 10 | 
             
              # Upload a file.
         | 
| 9 11 | 
             
              # @param source A source path, either an absolute path or a modulename/filename selector for a file in
         | 
| @@ -50,15 +52,13 @@ Puppet::Functions.create_function(:upload_file, Puppet::Functions::InternalFunct | |
| 50 52 | 
             
              end
         | 
| 51 53 |  | 
| 52 54 | 
             
              def upload_file_with_description(scope, source, destination, targets, description = nil, options = nil)
         | 
| 53 | 
            -
                options ||= {}
         | 
| 54 | 
            -
                options = options.merge('_description' => description) if description
         | 
| 55 | 
            -
             | 
| 56 55 | 
             
                unless Puppet[:tasks]
         | 
| 57 | 
            -
                  raise Puppet::ParseErrorWithIssue | 
| 58 | 
            -
                     | 
| 59 | 
            -
                  )
         | 
| 56 | 
            +
                  raise Puppet::ParseErrorWithIssue
         | 
| 57 | 
            +
                    .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'upload_file')
         | 
| 60 58 | 
             
                end
         | 
| 61 59 |  | 
| 60 | 
            +
                options ||= {}
         | 
| 61 | 
            +
                options = options.merge('_description' => description) if description
         | 
| 62 62 | 
             
                executor = Puppet.lookup(:bolt_executor) { nil }
         | 
| 63 63 | 
             
                inventory = Puppet.lookup(:bolt_inventory) { nil }
         | 
| 64 64 | 
             
                unless executor && inventory && Puppet.features.bolt?
         | 
| @@ -3,6 +3,8 @@ | |
| 3 3 | 
             
            require 'bolt/util'
         | 
| 4 4 |  | 
| 5 5 | 
             
            # Wait until all targets accept connections.
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            # **NOTE:** Not available in apply block
         | 
| 6 8 | 
             
            Puppet::Functions.create_function(:wait_until_available) do
         | 
| 7 9 | 
             
              # Wait until targets are available.
         | 
| 8 10 | 
             
              # @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
         | 
| @@ -17,14 +19,13 @@ Puppet::Functions.create_function(:wait_until_available) do | |
| 17 19 | 
             
              end
         | 
| 18 20 |  | 
| 19 21 | 
             
              def wait_until_available(targets, options = nil)
         | 
| 20 | 
            -
                options ||= {}
         | 
| 21 | 
            -
             | 
| 22 22 | 
             
                unless Puppet[:tasks]
         | 
| 23 | 
            -
                  raise Puppet::ParseErrorWithIssue | 
| 24 | 
            -
                     | 
| 25 | 
            -
             | 
| 23 | 
            +
                  raise Puppet::ParseErrorWithIssue
         | 
| 24 | 
            +
                    .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING,
         | 
| 25 | 
            +
                                          action: 'wait_until_available')
         | 
| 26 26 | 
             
                end
         | 
| 27 27 |  | 
| 28 | 
            +
                options ||= {}
         | 
| 28 29 | 
             
                executor = Puppet.lookup(:bolt_executor) { nil }
         | 
| 29 30 | 
             
                inventory = Puppet.lookup(:bolt_inventory) { nil }
         | 
| 30 31 | 
             
                unless executor && inventory && Puppet.features.bolt?
         | 
| @@ -5,6 +5,8 @@ | |
| 5 5 | 
             
            # Messages for actions within this block will be logged at `info` level instead
         | 
| 6 6 | 
             
            # of `notice`, so they will not be seen normally but # will still be present
         | 
| 7 7 | 
             
            # when `verbose` logging is requested.
         | 
| 8 | 
            +
            #
         | 
| 9 | 
            +
            # **NOTE:** Not available in apply block
         | 
| 8 10 | 
             
            Puppet::Functions.create_function(:without_default_logging) do
         | 
| 9 11 | 
             
              # @param block The block where action logging is suppressed.
         | 
| 10 12 | 
             
              # @return [Undef]
         | 
| @@ -20,6 +22,12 @@ Puppet::Functions.create_function(:without_default_logging) do | |
| 20 22 | 
             
              end
         | 
| 21 23 |  | 
| 22 24 | 
             
              def without_default_logging
         | 
| 25 | 
            +
                unless Puppet[:tasks]
         | 
| 26 | 
            +
                  raise Puppet::ParseErrorWithIssue
         | 
| 27 | 
            +
                    .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING,
         | 
| 28 | 
            +
                                          action: 'without_default_logging')
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 23 31 | 
             
                executor = Puppet.lookup(:bolt_executor) { nil }
         | 
| 24 32 | 
             
                executor.report_function_call('without_default_logging')
         | 
| 25 33 |  | 
    
        data/lib/bolt/config.rb
    CHANGED
    
    | @@ -36,29 +36,7 @@ module Bolt | |
| 36 36 |  | 
| 37 37 | 
             
                TRANSPORT_OPTIONS = %i[password run-as sudo-password extensions
         | 
| 38 38 | 
             
                                       private-key tty tmpdir user connect-timeout
         | 
| 39 | 
            -
                                       cacert token-file service-url].freeze
         | 
| 40 | 
            -
             | 
| 41 | 
            -
                # TODO: move these to the transport themselves
         | 
| 42 | 
            -
                TRANSPORT_SPECIFIC_DEFAULTS = {
         | 
| 43 | 
            -
                  ssh: {
         | 
| 44 | 
            -
                    'connect-timeout' => 10,
         | 
| 45 | 
            -
                    'host-key-check' => true,
         | 
| 46 | 
            -
                    'tty' => false
         | 
| 47 | 
            -
                  },
         | 
| 48 | 
            -
                  winrm: {
         | 
| 49 | 
            -
                    'connect-timeout' => 10,
         | 
| 50 | 
            -
                    'ssl' => true,
         | 
| 51 | 
            -
                    'ssl-verify' => true
         | 
| 52 | 
            -
                  },
         | 
| 53 | 
            -
                  pcp: {
         | 
| 54 | 
            -
                    'task-environment' => 'production'
         | 
| 55 | 
            -
                  },
         | 
| 56 | 
            -
                  local: {},
         | 
| 57 | 
            -
                  docker: {},
         | 
| 58 | 
            -
                  remote: {
         | 
| 59 | 
            -
                    'run-on' => 'localhost'
         | 
| 60 | 
            -
                  }
         | 
| 61 | 
            -
                }.freeze
         | 
| 39 | 
            +
                                       cacert token-file service-url interpreters file-protocol smb-port].freeze
         | 
| 62 40 |  | 
| 63 41 | 
             
                def self.default
         | 
| 64 42 | 
             
                  new(Bolt::Boltdir.new('.'), {})
         | 
| @@ -91,8 +69,9 @@ module Bolt | |
| 91 69 | 
             
                  @log = { 'console' => {} }
         | 
| 92 70 |  | 
| 93 71 | 
             
                  @transports = {}
         | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 72 | 
            +
             | 
| 73 | 
            +
                  TRANSPORTS.each do |key, transport|
         | 
| 74 | 
            +
                    @transports[key] = transport.default_options
         | 
| 96 75 | 
             
                  end
         | 
| 97 76 |  | 
| 98 77 | 
             
                  update_from_file(config_data)
         | 
| @@ -114,6 +93,12 @@ module Bolt | |
| 114 93 | 
             
                  Bolt::Util.deep_clone(self)
         | 
| 115 94 | 
             
                end
         | 
| 116 95 |  | 
| 96 | 
            +
                def normalize_interpreters(interpreters)
         | 
| 97 | 
            +
                  Bolt::Util.walk_keys(interpreters) do |key|
         | 
| 98 | 
            +
                    key.chars[0] == '.' ? key : '.' + key
         | 
| 99 | 
            +
                  end
         | 
| 100 | 
            +
                end
         | 
| 101 | 
            +
             | 
| 117 102 | 
             
                def normalize_log(target)
         | 
| 118 103 | 
             
                  return target if target == 'console'
         | 
| 119 104 | 
             
                  target = target[5..-1] if target.start_with?('file:')
         | 
| @@ -168,7 +153,10 @@ module Bolt | |
| 168 153 | 
             
                  TRANSPORTS.each do |key, impl|
         | 
| 169 154 | 
             
                    if data[key.to_s]
         | 
| 170 155 | 
             
                      selected = impl.filter_options(data[key.to_s])
         | 
| 171 | 
            -
                      @transports[key]. | 
| 156 | 
            +
                      @transports[key] = Bolt::Util.deep_merge(@transports[key], selected)
         | 
| 157 | 
            +
                    end
         | 
| 158 | 
            +
                    if @transports[key]['interpreters']
         | 
| 159 | 
            +
                      @transports[key]['interpreters'] = normalize_interpreters(@transports[key]['interpreters'])
         | 
| 172 160 | 
             
                    end
         | 
| 173 161 | 
             
                  end
         | 
| 174 162 | 
             
                end
         | 
    
        data/lib/bolt/pal.rb
    CHANGED
    
    
| @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Bolt
         | 
| 4 | 
            +
              class PAL
         | 
| 5 | 
            +
                module Issues
         | 
| 6 | 
            +
                  # Create issue using Issues api
         | 
| 7 | 
            +
                  PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING =
         | 
| 8 | 
            +
                    Puppet::Pops::Issues.issue :PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, :action do
         | 
| 9 | 
            +
                      "Plan language function '#{action}' cannot be used from declarative manifest code or apply blocks"
         | 
| 10 | 
            +
                    end
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
    
        data/lib/bolt/transport/base.rb
    CHANGED
    
    | @@ -48,6 +48,10 @@ module Bolt | |
| 48 48 | 
             
                          "self.options() or self.filter_options(unfiltered) must be implemented by the transport class"
         | 
| 49 49 | 
             
                  end
         | 
| 50 50 |  | 
| 51 | 
            +
                  def self.default_options
         | 
| 52 | 
            +
                    {}
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 51 55 | 
             
                  def self.filter_options(unfiltered)
         | 
| 52 56 | 
             
                    unfiltered.select { |k| options.include?(k) }
         | 
| 53 57 | 
             
                  end
         | 
| @@ -87,6 +91,10 @@ module Bolt | |
| 87 91 | 
             
                    impl
         | 
| 88 92 | 
             
                  end
         | 
| 89 93 |  | 
| 94 | 
            +
                  def select_interpreter(executable, interpreters)
         | 
| 95 | 
            +
                    interpreters[Pathname(executable).extname] if interpreters
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
             | 
| 90 98 | 
             
                  def reject_transport_options(target, options)
         | 
| 91 99 | 
             
                    if target.options['run-as']
         | 
| 92 100 | 
             
                      options.reject { |k, _v| k == '_run_as' }
         | 
| @@ -8,7 +8,7 @@ module Bolt | |
| 8 8 | 
             
              module Transport
         | 
| 9 9 | 
             
                class Docker < Base
         | 
| 10 10 | 
             
                  def self.options
         | 
| 11 | 
            -
                    %w[service-url service-options tmpdir]
         | 
| 11 | 
            +
                    %w[service-url service-options tmpdir interpreters]
         | 
| 12 12 | 
             
                  end
         | 
| 13 13 |  | 
| 14 14 | 
             
                  def provided_features
         | 
| @@ -48,7 +48,7 @@ module Bolt | |
| 48 48 |  | 
| 49 49 | 
             
                        _, stderr, exitcode = conn.execute('mv', tmpfile, destination, {})
         | 
| 50 50 | 
             
                        if exitcode != 0
         | 
| 51 | 
            -
                          message = "Could not move temporary file '#{tmpfile}' to #{destination}: #{stderr | 
| 51 | 
            +
                          message = "Could not move temporary file '#{tmpfile}' to #{destination}: #{stderr}"
         | 
| 52 52 | 
             
                          raise Bolt::Node::FileError.new(message, 'MV_ERROR')
         | 
| 53 53 | 
             
                        end
         | 
| 54 54 | 
             
                      end
         | 
| @@ -59,7 +59,7 @@ module Bolt | |
| 59 59 | 
             
                  def run_command(target, command, _options = {})
         | 
| 60 60 | 
             
                    with_connection(target) do |conn|
         | 
| 61 61 | 
             
                      stdout, stderr, exitcode = conn.execute(*Shellwords.split(command), {})
         | 
| 62 | 
            -
                      Bolt::Result.for_command(target, stdout | 
| 62 | 
            +
                      Bolt::Result.for_command(target, stdout, stderr, exitcode)
         | 
| 63 63 | 
             
                    end
         | 
| 64 64 | 
             
                  end
         | 
| 65 65 |  | 
| @@ -71,7 +71,7 @@ module Bolt | |
| 71 71 | 
             
                      conn.with_remote_tempdir do |dir|
         | 
| 72 72 | 
             
                        remote_path = conn.write_remote_executable(dir, script)
         | 
| 73 73 | 
             
                        stdout, stderr, exitcode = conn.execute(remote_path, *arguments, {})
         | 
| 74 | 
            -
                        Bolt::Result.for_command(target, stdout | 
| 74 | 
            +
                        Bolt::Result.for_command(target, stdout, stderr, exitcode)
         | 
| 75 75 | 
             
                      end
         | 
| 76 76 | 
             
                    end
         | 
| 77 77 | 
             
                  end
         | 
| @@ -87,7 +87,7 @@ module Bolt | |
| 87 87 | 
             
                    arguments = unwrap_sensitive_args(arguments)
         | 
| 88 88 | 
             
                    with_connection(target) do |conn|
         | 
| 89 89 | 
             
                      execute_options = {}
         | 
| 90 | 
            -
             | 
| 90 | 
            +
                      execute_options[:interpreter] = select_interpreter(executable, target.options['interpreters'])
         | 
| 91 91 | 
             
                      conn.with_remote_tempdir do |dir|
         | 
| 92 92 | 
             
                        if extra_files.empty?
         | 
| 93 93 | 
             
                          task_dir = dir
         | 
| @@ -112,7 +112,7 @@ module Bolt | |
| 112 112 | 
             
                        end
         | 
| 113 113 |  | 
| 114 114 | 
             
                        stdout, stderr, exitcode = conn.execute(remote_task_path, execute_options)
         | 
| 115 | 
            -
                        Bolt::Result.for_task(target, stdout | 
| 115 | 
            +
                        Bolt::Result.for_task(target, stdout, stderr, exitcode)
         | 
| 116 116 | 
             
                      end
         | 
| 117 117 | 
             
                    end
         | 
| 118 118 | 
             
                  end
         | 
| @@ -27,6 +27,7 @@ module Bolt | |
| 27 27 | 
             
                    end
         | 
| 28 28 |  | 
| 29 29 | 
             
                    def execute(*command, options)
         | 
| 30 | 
            +
                      command.unshift(options[:interpreter]) if options[:interpreter]
         | 
| 30 31 | 
             
                      if options[:environment]
         | 
| 31 32 | 
             
                        envs = options[:environment].map { |env, val| "#{env}=#{val}" }
         | 
| 32 33 | 
             
                        command = ['env'] + envs + command
         | 
| @@ -39,6 +40,8 @@ module Bolt | |
| 39 40 | 
             
                      else
         | 
| 40 41 | 
             
                        @logger.info { "Command failed with exit code #{result[2]}" }
         | 
| 41 42 | 
             
                      end
         | 
| 43 | 
            +
                      result[0] = result[0].join.force_encoding('UTF-8')
         | 
| 44 | 
            +
                      result[1] = result[1].join.force_encoding('UTF-8')
         | 
| 42 45 | 
             
                      result
         | 
| 43 46 | 
             
                    rescue StandardError
         | 
| 44 47 | 
             
                      @logger.debug { "Command aborted" }
         | 
| @@ -62,7 +65,7 @@ module Bolt | |
| 62 65 | 
             
                    def mkdirs(dirs)
         | 
| 63 66 | 
             
                      _, stderr, exitcode = execute('mkdir', '-p', *dirs, {})
         | 
| 64 67 | 
             
                      if exitcode != 0
         | 
| 65 | 
            -
                        message = "Could not create directories: #{stderr | 
| 68 | 
            +
                        message = "Could not create directories: #{stderr}"
         | 
| 66 69 | 
             
                        raise Bolt::Node::FileError.new(message, 'MKDIR_ERROR')
         | 
| 67 70 | 
             
                      end
         | 
| 68 71 | 
             
                    end
         | 
| @@ -73,7 +76,7 @@ module Bolt | |
| 73 76 |  | 
| 74 77 | 
             
                      stdout, stderr, exitcode = execute('mkdir', '-m', '700', tmppath, {})
         | 
| 75 78 | 
             
                      if exitcode != 0
         | 
| 76 | 
            -
                        raise Bolt::Node::FileError.new("Could not make tempdir: #{stderr | 
| 79 | 
            +
                        raise Bolt::Node::FileError.new("Could not make tempdir: #{stderr}", 'TEMPDIR_ERROR')
         | 
| 77 80 | 
             
                      end
         | 
| 78 81 | 
             
                      tmppath || stdout.first
         | 
| 79 82 | 
             
                    end
         | 
| @@ -85,7 +88,7 @@ module Bolt | |
| 85 88 | 
             
                      if dir
         | 
| 86 89 | 
             
                        _, stderr, exitcode = execute('rm', '-rf', dir, {})
         | 
| 87 90 | 
             
                        if exitcode != 0
         | 
| 88 | 
            -
                          @logger.warn("Failed to clean up tempdir '#{dir}': #{stderr | 
| 91 | 
            +
                          @logger.warn("Failed to clean up tempdir '#{dir}': #{stderr}")
         | 
| 89 92 | 
             
                        end
         | 
| 90 93 | 
             
                      end
         | 
| 91 94 | 
             
                    end
         | 
| @@ -101,7 +104,7 @@ module Bolt | |
| 101 104 | 
             
                    def make_executable(path)
         | 
| 102 105 | 
             
                      _, stderr, exitcode = execute('chmod', 'u+x', path, {})
         | 
| 103 106 | 
             
                      if exitcode != 0
         | 
| 104 | 
            -
                        message = "Could not make file '#{path}' executable: #{stderr | 
| 107 | 
            +
                        message = "Could not make file '#{path}' executable: #{stderr}"
         | 
| 105 108 | 
             
                        raise Bolt::Node::FileError.new(message, 'CHMOD_ERROR')
         | 
| 106 109 | 
             
                      end
         | 
| 107 110 | 
             
                    end
         | 
    
        data/lib/bolt/transport/local.rb
    CHANGED
    
    | @@ -11,7 +11,13 @@ module Bolt | |
| 11 11 | 
             
              module Transport
         | 
| 12 12 | 
             
                class Local < Base
         | 
| 13 13 | 
             
                  def self.options
         | 
| 14 | 
            -
                    %w[tmpdir]
         | 
| 14 | 
            +
                    %w[tmpdir interpreters]
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  def self.default_options
         | 
| 18 | 
            +
                    {
         | 
| 19 | 
            +
                      'interpreters' => { '.rb' => RbConfig.ruby }
         | 
| 20 | 
            +
                    }
         | 
| 15 21 | 
             
                  end
         | 
| 16 22 |  | 
| 17 23 | 
             
                  def provided_features
         | 
| @@ -129,8 +135,10 @@ module Bolt | |
| 129 135 | 
             
                      copy_file(executable, script)
         | 
| 130 136 | 
             
                      File.chmod(0o750, script)
         | 
| 131 137 |  | 
| 138 | 
            +
                      interpreter = select_interpreter(script, target.options['interpreters'])
         | 
| 139 | 
            +
                      interpreter_debug = interpreter ? " using '#{interpreter}' interpreter" : nil
         | 
| 132 140 | 
             
                      # log the arguments with sensitive data redacted, do NOT log unwrapped_arguments
         | 
| 133 | 
            -
                      logger.debug("Running '#{script}' with #{arguments}")
         | 
| 141 | 
            +
                      logger.debug("Running '#{script}' with #{arguments}#{interpreter_debug}")
         | 
| 134 142 | 
             
                      unwrapped_arguments = unwrap_sensitive_args(arguments)
         | 
| 135 143 |  | 
| 136 144 | 
             
                      stdin = STDIN_METHODS.include?(input_method) ? JSON.dump(unwrapped_arguments) : nil
         | 
| @@ -146,25 +154,32 @@ module Bolt | |
| 146 154 | 
             
                          environment_params = ""
         | 
| 147 155 | 
             
                        end
         | 
| 148 156 |  | 
| 149 | 
            -
                         | 
| 150 | 
            -
                           | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 157 | 
            +
                        if Powershell.powershell_file?(script) && stdin.nil?
         | 
| 158 | 
            +
                          command = Powershell.run_ps_task(arguments, script, input_method)
         | 
| 159 | 
            +
                          command = environment_params + Powershell.shell_init + command
         | 
| 160 | 
            +
                          interpreter ||= 'powershell.exe'
         | 
| 161 | 
            +
                          output =
         | 
| 153 162 | 
             
                            if input_method == 'powershell'
         | 
| 154 | 
            -
                              @conn.execute(command, dir: dir,  | 
| 163 | 
            +
                              @conn.execute(command, dir: dir, interpreter: interpreter)
         | 
| 155 164 | 
             
                            else
         | 
| 156 | 
            -
                              @conn.execute(command, dir: dir, stdin: stdin,  | 
| 165 | 
            +
                              @conn.execute(command, dir: dir, stdin: stdin, interpreter: interpreter)
         | 
| 157 166 | 
             
                            end
         | 
| 167 | 
            +
                        end
         | 
| 168 | 
            +
                        unless output
         | 
| 169 | 
            +
                          if interpreter
         | 
| 170 | 
            +
                            env = ENVIRONMENT_METHODS.include?(input_method) ? envify_params(unwrapped_arguments) : nil
         | 
| 171 | 
            +
                            output = @conn.execute(script, stdin: stdin, env: env, dir: dir, interpreter: interpreter)
         | 
| 158 172 | 
             
                          else
         | 
| 159 173 | 
             
                            path, args = *Powershell.process_from_extension(script)
         | 
| 160 174 | 
             
                            command = args.unshift(path).join(' ')
         | 
| 161 175 | 
             
                            command = environment_params + Powershell.shell_init + command
         | 
| 162 | 
            -
                            @conn.execute(command, dir: dir, stdin: stdin,  | 
| 176 | 
            +
                            output = @conn.execute(command, dir: dir, stdin: stdin, interpreter: 'powershell.exe')
         | 
| 163 177 | 
             
                          end
         | 
| 178 | 
            +
                        end
         | 
| 164 179 | 
             
                      else
         | 
| 165 180 | 
             
                        # POSIX
         | 
| 166 181 | 
             
                        env = ENVIRONMENT_METHODS.include?(input_method) ? envify_params(unwrapped_arguments) : nil
         | 
| 167 | 
            -
                        output = @conn.execute(script, stdin: stdin, env: env, dir: dir)
         | 
| 182 | 
            +
                        output = @conn.execute(script, stdin: stdin, env: env, dir: dir, interpreter: interpreter)
         | 
| 168 183 | 
             
                      end
         | 
| 169 184 | 
             
                      Bolt::Result.for_task(target, output.stdout.string, output.stderr.string, output.exit_code)
         | 
| 170 185 | 
             
                    end
         | 
    
        data/lib/bolt/transport/orch.rb
    CHANGED
    
    
    
        data/lib/bolt/transport/ssh.rb
    CHANGED
    
    | @@ -10,7 +10,15 @@ module Bolt | |
| 10 10 | 
             
                class SSH < Base
         | 
| 11 11 | 
             
                  def self.options
         | 
| 12 12 | 
             
                    %w[port user password sudo-password private-key host-key-check
         | 
| 13 | 
            -
                       connect-timeout tmpdir run-as tty run-as-command proxyjump]
         | 
| 13 | 
            +
                       connect-timeout tmpdir run-as tty run-as-command proxyjump interpreters]
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def self.default_options
         | 
| 17 | 
            +
                    {
         | 
| 18 | 
            +
                      'connect-timeout' => 10,
         | 
| 19 | 
            +
                      'host-key-check' => true,
         | 
| 20 | 
            +
                      'tty' => false
         | 
| 21 | 
            +
                    }
         | 
| 14 22 | 
             
                  end
         | 
| 15 23 |  | 
| 16 24 | 
             
                  def provided_features
         | 
| @@ -169,6 +177,7 @@ module Bolt | |
| 169 177 | 
             
                          dir.chown(conn.run_as)
         | 
| 170 178 |  | 
| 171 179 | 
             
                          execute_options[:sudoable] = true if conn.run_as
         | 
| 180 | 
            +
                          execute_options[:interpreter] = select_interpreter(executable, target.options['interpreters'])
         | 
| 172 181 | 
             
                          output = conn.execute(command, execute_options)
         | 
| 173 182 | 
             
                        end
         | 
| 174 183 | 
             
                        Bolt::Result.for_task(target, output.stdout.string,
         | 
| @@ -227,6 +227,10 @@ module Bolt | |
| 227 227 | 
             
                      escalate = sudoable && run_as && @user != run_as
         | 
| 228 228 | 
             
                      use_sudo = escalate && @target.options['run-as-command'].nil?
         | 
| 229 229 |  | 
| 230 | 
            +
                      if options[:interpreter]
         | 
| 231 | 
            +
                        command.is_a?(Array) ? command.unshift(options[:interpreter]) : [options[:interpreter], command]
         | 
| 232 | 
            +
                      end
         | 
| 233 | 
            +
             | 
| 230 234 | 
             
                      command_str = command.is_a?(String) ? command : Shellwords.shelljoin(command)
         | 
| 231 235 | 
             
                      if escalate
         | 
| 232 236 | 
             
                        if use_sudo
         | 
    
        data/lib/bolt/transport/winrm.rb
    CHANGED
    
    | @@ -8,7 +8,19 @@ module Bolt | |
| 8 8 | 
             
              module Transport
         | 
| 9 9 | 
             
                class WinRM < Base
         | 
| 10 10 | 
             
                  def self.options
         | 
| 11 | 
            -
                    %w[ | 
| 11 | 
            +
                    %w[
         | 
| 12 | 
            +
                      port user password connect-timeout ssl ssl-verify tmpdir cacert
         | 
| 13 | 
            +
                      extensions interpreters file-protocol smb-port
         | 
| 14 | 
            +
                    ]
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  def self.default_options
         | 
| 18 | 
            +
                    {
         | 
| 19 | 
            +
                      'connect-timeout' => 10,
         | 
| 20 | 
            +
                      'ssl' => true,
         | 
| 21 | 
            +
                      'ssl-verify' => true,
         | 
| 22 | 
            +
                      'file-protocol' => 'winrm'
         | 
| 23 | 
            +
                    }
         | 
| 12 24 | 
             
                  end
         | 
| 13 25 |  | 
| 14 26 | 
             
                  def provided_features
         | 
| @@ -26,6 +38,10 @@ module Bolt | |
| 26 38 | 
             
                      raise Bolt::ValidationError, 'ssl option must be a Boolean true or false'
         | 
| 27 39 | 
             
                    end
         | 
| 28 40 |  | 
| 41 | 
            +
                    if ssl_flag && (options['file-protocol'] == 'smb')
         | 
| 42 | 
            +
                      raise Bolt::ValidationError, 'SMB file transfers are not allowed with SSL enabled'
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
             | 
| 29 45 | 
             
                    ssl_verify_flag = options['ssl-verify']
         | 
| 30 46 | 
             
                    unless !!ssl_verify_flag == ssl_verify_flag
         | 
| 31 47 | 
             
                      raise Bolt::ValidationError, 'ssl-verify option must be a Boolean true or false'
         | 
| @@ -135,7 +151,13 @@ module Bolt | |
| 135 151 | 
             
                          if Powershell.powershell_file?(remote_task_path) && stdin.nil?
         | 
| 136 152 | 
             
                            conn.execute(Powershell.run_ps_task(arguments, remote_task_path, input_method))
         | 
| 137 153 | 
             
                          else
         | 
| 138 | 
            -
                             | 
| 154 | 
            +
                            interpreter = select_interpreter(remote_task_path, target.options['interpreters'])
         | 
| 155 | 
            +
                            if interpreter
         | 
| 156 | 
            +
                              path = interpreter
         | 
| 157 | 
            +
                              args = [remote_task_path]
         | 
| 158 | 
            +
                            else
         | 
| 159 | 
            +
                              path, args = *Powershell.process_from_extension(remote_task_path)
         | 
| 160 | 
            +
                            end
         | 
| 139 161 | 
             
                            conn.execute_process(path, args, stdin)
         | 
| 140 162 | 
             
                          end
         | 
| 141 163 |  | 
| @@ -2,6 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            require 'bolt/node/errors'
         | 
| 4 4 | 
             
            require 'bolt/node/output'
         | 
| 5 | 
            +
            require 'ruby_smb'
         | 
| 5 6 |  | 
| 6 7 | 
             
            module Bolt
         | 
| 7 8 | 
             
              module Transport
         | 
| @@ -17,9 +18,9 @@ module Bolt | |
| 17 18 | 
             
                      default_port = target.options['ssl'] ? HTTPS_PORT : HTTP_PORT
         | 
| 18 19 | 
             
                      @port = @target.port || default_port
         | 
| 19 20 | 
             
                      @user = @target.user
         | 
| 20 | 
            -
             | 
| 21 | 
            -
                      # Accept a single entry or a list, ensure each is prefixed with '.'
         | 
| 21 | 
            +
                      # Build set of extensions from extensions config as well as interpreters
         | 
| 22 22 | 
             
                      extensions = [target.options['extensions'] || []].flatten.map { |ext| ext[0] != '.' ? '.' + ext : ext }
         | 
| 23 | 
            +
                      extensions += target.options['interpreters'].keys if target.options['interpreters']
         | 
| 23 24 | 
             
                      @extensions = DEFAULT_EXTENSIONS.to_set.merge(extensions)
         | 
| 24 25 |  | 
| 25 26 | 
             
                      @logger = Logging.logger[@target.host]
         | 
| @@ -93,6 +94,7 @@ module Bolt | |
| 93 94 |  | 
| 94 95 | 
             
                    def disconnect
         | 
| 95 96 | 
             
                      @session&.close
         | 
| 97 | 
            +
                      @client&.disconnect!
         | 
| 96 98 | 
             
                      @logger.debug { "Closed session" }
         | 
| 97 99 | 
             
                    end
         | 
| 98 100 |  | 
| @@ -141,12 +143,47 @@ module Bolt | |
| 141 143 | 
             
                    end
         | 
| 142 144 |  | 
| 143 145 | 
             
                    def write_remote_file(source, destination)
         | 
| 146 | 
            +
                      if target.options['file-protocol'] == 'smb'
         | 
| 147 | 
            +
                        write_remote_file_smb(source, destination)
         | 
| 148 | 
            +
                      else
         | 
| 149 | 
            +
                        write_remote_file_winrm(source, destination)
         | 
| 150 | 
            +
                      end
         | 
| 151 | 
            +
                    end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                    def write_remote_file_winrm(source, destination)
         | 
| 144 154 | 
             
                      fs = ::WinRM::FS::FileManager.new(@connection)
         | 
| 145 155 | 
             
                      fs.upload(source, destination)
         | 
| 146 156 | 
             
                    rescue StandardError => e
         | 
| 147 157 | 
             
                      raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
         | 
| 148 158 | 
             
                    end
         | 
| 149 159 |  | 
| 160 | 
            +
                    def write_remote_file_smb(source, destination)
         | 
| 161 | 
            +
                      win_dest = destination.tr('/', '\\')
         | 
| 162 | 
            +
                      if (md = win_dest.match(/^([a-z]):\\(.*)/i))
         | 
| 163 | 
            +
                        # if drive, use admin share for that drive, so path is '\\host\C$'
         | 
| 164 | 
            +
                        path = "\\\\#{@target.host}\\#{md[1]}$"
         | 
| 165 | 
            +
                        dest = md[2]
         | 
| 166 | 
            +
                      elsif (md = win_dest.match(/^(\\\\[^\\]+\\[^\\]+)\\(.*)/))
         | 
| 167 | 
            +
                        # if unc, path is '\\host\share'
         | 
| 168 | 
            +
                        path = md[1]
         | 
| 169 | 
            +
                        dest = md[2]
         | 
| 170 | 
            +
                      else
         | 
| 171 | 
            +
                        raise ArgumentError, "Unknown destination '#{destination}'"
         | 
| 172 | 
            +
                      end
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                      client = smb_client_login
         | 
| 175 | 
            +
                      tree = client.tree_connect(path)
         | 
| 176 | 
            +
                      begin
         | 
| 177 | 
            +
                        write_remote_file_smb_recursive(tree, source, dest)
         | 
| 178 | 
            +
                      ensure
         | 
| 179 | 
            +
                        tree.disconnect!
         | 
| 180 | 
            +
                      end
         | 
| 181 | 
            +
                    rescue ::RubySMB::Error::UnexpectedStatusCode => e
         | 
| 182 | 
            +
                      raise Bolt::Node::FileError.new("SMB Error: #{e.message}", 'WRITE_ERROR')
         | 
| 183 | 
            +
                    rescue StandardError => e
         | 
| 184 | 
            +
                      raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
         | 
| 185 | 
            +
                    end
         | 
| 186 | 
            +
             | 
| 150 187 | 
             
                    def make_tempdir
         | 
| 151 188 | 
             
                      find_parent = target.options['tmpdir'] ? "\"#{target.options['tmpdir']}\"" : '[System.IO.Path]::GetTempPath()'
         | 
| 152 189 | 
             
                      result = execute(Powershell.make_tempdir(find_parent))
         | 
| @@ -184,6 +221,79 @@ module Bolt | |
| 184 221 | 
             
                      write_remote_file(content, remote_path)
         | 
| 185 222 | 
             
                      remote_path
         | 
| 186 223 | 
             
                    end
         | 
| 224 | 
            +
             | 
| 225 | 
            +
                    private
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                    def smb_client_login
         | 
| 228 | 
            +
                      return @client if @client
         | 
| 229 | 
            +
             | 
| 230 | 
            +
                      dispatcher = RubySMB::Dispatcher::Socket.new(smb_socket_connect)
         | 
| 231 | 
            +
                      @client = RubySMB::Client.new(dispatcher, smb1: false, smb2: true, username: @user, password: target.password)
         | 
| 232 | 
            +
                      status = @client.login
         | 
| 233 | 
            +
                      case status
         | 
| 234 | 
            +
                      when WindowsError::NTStatus::STATUS_SUCCESS
         | 
| 235 | 
            +
                        @logger.debug { "Connected to #{@client.dns_host_name}" }
         | 
| 236 | 
            +
                      when WindowsError::NTStatus::STATUS_LOGON_FAILURE
         | 
| 237 | 
            +
                        raise Bolt::Node::ConnectError.new(
         | 
| 238 | 
            +
                          "SMB authentication failed for #{target.host}",
         | 
| 239 | 
            +
                          'AUTH_ERROR'
         | 
| 240 | 
            +
                        )
         | 
| 241 | 
            +
                      else
         | 
| 242 | 
            +
                        raise Bolt::Node::ConnectError.new(
         | 
| 243 | 
            +
                          "Failed to connect to #{target.host} using SMB: #{status.description}",
         | 
| 244 | 
            +
                          'CONNECT_ERROR'
         | 
| 245 | 
            +
                        )
         | 
| 246 | 
            +
                      end
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                      @client
         | 
| 249 | 
            +
                    end
         | 
| 250 | 
            +
             | 
| 251 | 
            +
                    SMB_PORT = 445
         | 
| 252 | 
            +
             | 
| 253 | 
            +
                    def smb_socket_connect
         | 
| 254 | 
            +
                      # It's lame that TCPSocket doesn't take a connect timeout
         | 
| 255 | 
            +
                      # Using Timeout.timeout is bad, but is done elsewhere...
         | 
| 256 | 
            +
                      Timeout.timeout(target.options['connect-timeout']) do
         | 
| 257 | 
            +
                        TCPSocket.new(target.host, target.options['smb-port'] || SMB_PORT)
         | 
| 258 | 
            +
                      end
         | 
| 259 | 
            +
                    rescue Errno::ECONNREFUSED => e
         | 
| 260 | 
            +
                      # handle this to prevent obscuring error message as SMB problem
         | 
| 261 | 
            +
                      raise Bolt::Node::ConnectError.new(
         | 
| 262 | 
            +
                        "Failed to connect to #{target.host} using SMB: #{e.message}",
         | 
| 263 | 
            +
                        'CONNECT_ERROR'
         | 
| 264 | 
            +
                      )
         | 
| 265 | 
            +
                    rescue Timeout::Error
         | 
| 266 | 
            +
                      raise Bolt::Node::ConnectError.new(
         | 
| 267 | 
            +
                        "Timeout after #{target.options['connect-timeout']} seconds connecting to #{target.host}",
         | 
| 268 | 
            +
                        'CONNECT_ERROR'
         | 
| 269 | 
            +
                      )
         | 
| 270 | 
            +
                    end
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                    def write_remote_file_smb_recursive(tree, source, dest)
         | 
| 273 | 
            +
                      if Dir.exist?(source)
         | 
| 274 | 
            +
                        tree.open_directory(directory: dest, write: true, disposition: ::RubySMB::Dispositions::FILE_OPEN_IF)
         | 
| 275 | 
            +
             | 
| 276 | 
            +
                        (Dir.entries(source) - ['.', '..']).each do |child|
         | 
| 277 | 
            +
                          child_dest = dest + '\\' + child
         | 
| 278 | 
            +
                          write_remote_file_smb_recursive(tree, File.join(source, child), child_dest)
         | 
| 279 | 
            +
                        end
         | 
| 280 | 
            +
                        return
         | 
| 281 | 
            +
                      end
         | 
| 282 | 
            +
             | 
| 283 | 
            +
                      file = tree.open_file(filename: dest, write: true, disposition: ::RubySMB::Dispositions::FILE_OVERWRITE_IF)
         | 
| 284 | 
            +
                      begin
         | 
| 285 | 
            +
                        # `file` doesn't derive from IO, so can't use IO.copy_stream
         | 
| 286 | 
            +
                        File.open(source, 'rb') do |f|
         | 
| 287 | 
            +
                          pos = 0
         | 
| 288 | 
            +
                          while (buf = f.read(8 * 1024 * 1024))
         | 
| 289 | 
            +
                            file.write(data: buf, offset: pos)
         | 
| 290 | 
            +
                            pos += buf.length
         | 
| 291 | 
            +
                          end
         | 
| 292 | 
            +
                        end
         | 
| 293 | 
            +
                      ensure
         | 
| 294 | 
            +
                        file.close
         | 
| 295 | 
            +
                      end
         | 
| 296 | 
            +
                    end
         | 
| 187 297 | 
             
                  end
         | 
| 188 298 | 
             
                end
         | 
| 189 299 | 
             
              end
         | 
    
        data/lib/bolt/version.rb
    CHANGED
    
    
| @@ -47,8 +47,22 @@ | |
| 47 47 | 
             
                    "extensions": {
         | 
| 48 48 | 
             
                      "type": "array",
         | 
| 49 49 | 
             
                      "description": "List of file extensions that are accepted for scripts or tasks"
         | 
| 50 | 
            +
                    },
         | 
| 51 | 
            +
                    "interpreters": {
         | 
| 52 | 
            +
                      "type": "object",
         | 
| 53 | 
            +
                      "description": "Map of file extensions to remote executable"
         | 
| 54 | 
            +
                    },
         | 
| 55 | 
            +
                    "file-protocol": {
         | 
| 56 | 
            +
                      "type": "string",
         | 
| 57 | 
            +
                      "enum": ["winrm", "smb"],
         | 
| 58 | 
            +
                      "description": "Protocol for file transfer, WinRM or SMB"
         | 
| 59 | 
            +
                    },
         | 
| 60 | 
            +
                    "smb-port": {
         | 
| 61 | 
            +
                      "type": "integer",
         | 
| 62 | 
            +
                      "description": "Port for SMB protocol"
         | 
| 50 63 | 
             
                    }
         | 
| 51 64 | 
             
                  },
         | 
| 65 | 
            +
             | 
| 52 66 | 
             
                  "required": ["hostname", "user", "password"],
         | 
| 53 67 | 
             
                  "additionalProperties": false
         | 
| 54 68 | 
             
                },
         | 
    
        data/lib/bolt_spec/plans.rb
    CHANGED
    
    | @@ -32,6 +32,11 @@ require 'bolt/pal' | |
| 32 32 | 
             
            #
         | 
| 33 33 | 
             
            # Configuration
         | 
| 34 34 | 
             
            #
         | 
| 35 | 
            +
            #  To configure Puppet and Bolt at the beginning of tests, add the following
         | 
| 36 | 
            +
            #  line to your spec_helper.rb:
         | 
| 37 | 
            +
            #
         | 
| 38 | 
            +
            #  BoltSpec::Plans.init
         | 
| 39 | 
            +
            #
         | 
| 35 40 | 
             
            #  By default the plan helpers use the modulepath set up for rspec-puppet and
         | 
| 36 41 | 
             
            #  an otherwise empty bolt config and inventory. To create your own values for
         | 
| 37 42 | 
             
            #  these override the modulepath, config, or inventory methods.
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: bolt
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.13.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Puppet
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2019-02- | 
| 11 | 
            +
            date: 2019-02-27 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: addressable
         | 
| @@ -184,6 +184,20 @@ dependencies: | |
| 184 184 | 
             
                - - "~>"
         | 
| 185 185 | 
             
                  - !ruby/object:Gem::Version
         | 
| 186 186 | 
             
                    version: '2.6'
         | 
| 187 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 188 | 
            +
              name: ruby_smb
         | 
| 189 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 190 | 
            +
                requirements:
         | 
| 191 | 
            +
                - - "~>"
         | 
| 192 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 193 | 
            +
                    version: '1.0'
         | 
| 194 | 
            +
              type: :runtime
         | 
| 195 | 
            +
              prerelease: false
         | 
| 196 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 197 | 
            +
                requirements:
         | 
| 198 | 
            +
                - - "~>"
         | 
| 199 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 200 | 
            +
                    version: '1.0'
         | 
| 187 201 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 188 202 | 
             
              name: terminal-table
         | 
| 189 203 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -345,6 +359,7 @@ files: | |
| 345 359 | 
             
            - lib/bolt/outputter/human.rb
         | 
| 346 360 | 
             
            - lib/bolt/outputter/json.rb
         | 
| 347 361 | 
             
            - lib/bolt/pal.rb
         | 
| 362 | 
            +
            - lib/bolt/pal/issues.rb
         | 
| 348 363 | 
             
            - lib/bolt/pal/logging.rb
         | 
| 349 364 | 
             
            - lib/bolt/plan_result.rb
         | 
| 350 365 | 
             
            - lib/bolt/puppetdb.rb
         |