bolt 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Puppetfile +2 -1
- data/lib/bolt/executor.rb +1 -2
- data/lib/bolt/pal/yaml_plan/evaluator.rb +22 -14
- data/lib/bolt/pal/yaml_plan/step.rb +11 -4
- data/lib/bolt/pal/yaml_plan/step/command.rb +2 -2
- data/lib/bolt/pal/yaml_plan/step/resources.rb +3 -3
- data/lib/bolt/pal/yaml_plan/step/script.rb +2 -2
- data/lib/bolt/pal/yaml_plan/step/task.rb +2 -2
- data/lib/bolt/pal/yaml_plan/step/upload.rb +2 -2
- data/lib/bolt/plugin.rb +2 -1
- data/lib/bolt/shell.rb +1 -0
- data/lib/bolt/shell/powershell.rb +263 -0
- data/lib/bolt/shell/powershell/snippets.rb +273 -0
- data/lib/bolt/transport/local/connection.rb +11 -1
- data/lib/bolt/transport/winrm.rb +1 -118
- data/lib/bolt/transport/winrm/connection.rb +27 -78
- data/lib/bolt/version.rb +1 -1
- metadata +18 -4
- data/lib/bolt/transport/local_windows.rb +0 -189
- data/lib/bolt/transport/powershell.rb +0 -337
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: ca1602020625348ee5ba03e246feeb9263ca10fdcda10e13bba36eb674dcdfba
         | 
| 4 | 
            +
              data.tar.gz: bdde3554a2406f427f57d02f2e0063e592b7ceba235656c406e9f1ece3899c75
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 13bcc53364488a1f37ebc19821c6429bcbcc055a543f9b581352b03c403793579290af88be0b92416506c3ddd9f23e775c7d9b9a6d2369aed3c38b0ef162da8f
         | 
| 7 | 
            +
              data.tar.gz: ea09c6b92cc08d1cd893c136775070df2f6d79097663c58a96212bfdcf23984fe39fd5690d706f4802a61cd384bfe8139e446914506938605ba6d4771f272d63
         | 
    
        data/Puppetfile
    CHANGED
    
    | @@ -30,10 +30,11 @@ mod 'puppetlabs-ruby_task_helper', '0.5.1' | |
| 30 30 | 
             
            mod 'puppetlabs-ruby_plugin_helper', '0.1.0'
         | 
| 31 31 |  | 
| 32 32 | 
             
            # Plugin modules
         | 
| 33 | 
            +
            mod 'puppetlabs-aws_inventory', '0.5.0'
         | 
| 33 34 | 
             
            mod 'puppetlabs-azure_inventory', '0.3.0'
         | 
| 35 | 
            +
            mod 'puppetlabs-gcloud_inventory', '0.1.0'
         | 
| 34 36 | 
             
            mod 'puppetlabs-terraform', '0.5.0'
         | 
| 35 37 | 
             
            mod 'puppetlabs-vault', '0.3.0'
         | 
| 36 | 
            -
            mod 'puppetlabs-aws_inventory', '0.5.0'
         | 
| 37 38 | 
             
            mod 'puppetlabs-yaml', '0.2.0'
         | 
| 38 39 |  | 
| 39 40 | 
             
            # If we don't list these modules explicitly, r10k will purge them
         | 
    
        data/lib/bolt/executor.rb
    CHANGED
    
    | @@ -15,7 +15,6 @@ require 'bolt/transport/ssh' | |
| 15 15 | 
             
            require 'bolt/transport/winrm'
         | 
| 16 16 | 
             
            require 'bolt/transport/orch'
         | 
| 17 17 | 
             
            require 'bolt/transport/local'
         | 
| 18 | 
            -
            require 'bolt/transport/local_windows'
         | 
| 19 18 | 
             
            require 'bolt/transport/docker'
         | 
| 20 19 | 
             
            require 'bolt/transport/remote'
         | 
| 21 20 |  | 
| @@ -24,7 +23,7 @@ module Bolt | |
| 24 23 | 
             
                ssh: Bolt::Transport::SSH,
         | 
| 25 24 | 
             
                winrm: Bolt::Transport::WinRM,
         | 
| 26 25 | 
             
                pcp: Bolt::Transport::Orch,
         | 
| 27 | 
            -
                local: Bolt:: | 
| 26 | 
            +
                local: Bolt::Transport::Local,
         | 
| 28 27 | 
             
                docker: Bolt::Transport::Docker,
         | 
| 29 28 | 
             
                remote: Bolt::Transport::Remote
         | 
| 30 29 | 
             
              }.freeze
         | 
| @@ -24,14 +24,14 @@ module Bolt | |
| 24 24 |  | 
| 25 25 | 
             
                    def task_step(scope, step)
         | 
| 26 26 | 
             
                      task = step['task']
         | 
| 27 | 
            -
                       | 
| 27 | 
            +
                      targets = step['targets'] || step['target']
         | 
| 28 28 | 
             
                      description = step['description']
         | 
| 29 29 | 
             
                      params = step['parameters'] || {}
         | 
| 30 30 |  | 
| 31 31 | 
             
                      args = if description
         | 
| 32 | 
            -
                               [task,  | 
| 32 | 
            +
                               [task, targets, description, params]
         | 
| 33 33 | 
             
                             else
         | 
| 34 | 
            -
                               [task,  | 
| 34 | 
            +
                               [task, targets, params]
         | 
| 35 35 | 
             
                             end
         | 
| 36 36 |  | 
| 37 37 | 
             
                      scope.call_function('run_task', args)
         | 
| @@ -48,15 +48,15 @@ module Bolt | |
| 48 48 |  | 
| 49 49 | 
             
                    def script_step(scope, step)
         | 
| 50 50 | 
             
                      script = step['script']
         | 
| 51 | 
            -
                       | 
| 51 | 
            +
                      targets = step['targets'] || step['target']
         | 
| 52 52 | 
             
                      description = step['description']
         | 
| 53 53 | 
             
                      arguments = step['arguments'] || []
         | 
| 54 54 |  | 
| 55 55 | 
             
                      options = { 'arguments' => arguments }
         | 
| 56 56 | 
             
                      args = if description
         | 
| 57 | 
            -
                               [script,  | 
| 57 | 
            +
                               [script, targets, description, options]
         | 
| 58 58 | 
             
                             else
         | 
| 59 | 
            -
                               [script,  | 
| 59 | 
            +
                               [script, targets, options]
         | 
| 60 60 | 
             
                             end
         | 
| 61 61 |  | 
| 62 62 | 
             
                      scope.call_function('run_script', args)
         | 
| @@ -64,10 +64,10 @@ module Bolt | |
| 64 64 |  | 
| 65 65 | 
             
                    def command_step(scope, step)
         | 
| 66 66 | 
             
                      command = step['command']
         | 
| 67 | 
            -
                       | 
| 67 | 
            +
                      targets = step['targets'] || step['target']
         | 
| 68 68 | 
             
                      description = step['description']
         | 
| 69 69 |  | 
| 70 | 
            -
                      args = [command,  | 
| 70 | 
            +
                      args = [command, targets]
         | 
| 71 71 | 
             
                      args << description if description
         | 
| 72 72 | 
             
                      scope.call_function('run_command', args)
         | 
| 73 73 | 
             
                    end
         | 
| @@ -75,10 +75,10 @@ module Bolt | |
| 75 75 | 
             
                    def upload_step(scope, step)
         | 
| 76 76 | 
             
                      source = step['source']
         | 
| 77 77 | 
             
                      destination = step['destination']
         | 
| 78 | 
            -
                       | 
| 78 | 
            +
                      targets = step['targets'] || step['target']
         | 
| 79 79 | 
             
                      description = step['description']
         | 
| 80 80 |  | 
| 81 | 
            -
                      args = [source, destination,  | 
| 81 | 
            +
                      args = [source, destination, targets]
         | 
| 82 82 | 
             
                      args << description if description
         | 
| 83 83 | 
             
                      scope.call_function('upload_file', args)
         | 
| 84 84 | 
             
                    end
         | 
| @@ -88,11 +88,13 @@ module Bolt | |
| 88 88 | 
             
                    end
         | 
| 89 89 |  | 
| 90 90 | 
             
                    def resources_step(scope, step)
         | 
| 91 | 
            +
                      targets = step['targets'] || step['target']
         | 
| 92 | 
            +
             | 
| 91 93 | 
             
                      # TODO: Only call apply_prep when needed
         | 
| 92 | 
            -
                      scope.call_function('apply_prep',  | 
| 94 | 
            +
                      scope.call_function('apply_prep', targets)
         | 
| 93 95 | 
             
                      manifest = generate_manifest(step['resources'])
         | 
| 94 96 |  | 
| 95 | 
            -
                      apply_manifest(scope,  | 
| 97 | 
            +
                      apply_manifest(scope, targets, manifest)
         | 
| 96 98 | 
             
                    end
         | 
| 97 99 |  | 
| 98 100 | 
             
                    def generate_manifest(resources)
         | 
| @@ -127,16 +129,22 @@ module Bolt | |
| 127 129 | 
             
                      MANIFEST
         | 
| 128 130 | 
             
                    end
         | 
| 129 131 |  | 
| 130 | 
            -
                    def apply_manifest(scope,  | 
| 132 | 
            +
                    def apply_manifest(scope, targets, manifest)
         | 
| 131 133 | 
             
                      ast = @evaluator.parse_string(manifest)
         | 
| 132 134 | 
             
                      apply_block = ast.body.body
         | 
| 133 135 | 
             
                      applicator = Puppet.lookup(:apply_executor)
         | 
| 134 | 
            -
                      applicator.apply([ | 
| 136 | 
            +
                      applicator.apply([targets], apply_block, scope)
         | 
| 135 137 | 
             
                    end
         | 
| 136 138 |  | 
| 137 139 | 
             
                    # This is the method that Puppet calls to evaluate the plan. The name
         | 
| 138 140 | 
             
                    # makes more sense for .pp plans.
         | 
| 139 141 | 
             
                    def evaluate_block_with_bindings(closure_scope, args_hash, plan)
         | 
| 142 | 
            +
                      if plan.steps.any? { |step| step.body.key?('target') }
         | 
| 143 | 
            +
                        msg = "The 'target' parameter for YAML plan steps is deprecated and will be removed "\
         | 
| 144 | 
            +
                              "in a future version of Bolt. Use the 'targets' parameter instead."
         | 
| 145 | 
            +
                        @logger.warn(msg)
         | 
| 146 | 
            +
                      end
         | 
| 147 | 
            +
             | 
| 140 148 | 
             
                      plan_result = closure_scope.with_local_scope(args_hash) do |scope|
         | 
| 141 149 | 
             
                        plan.steps.each do |step|
         | 
| 142 150 | 
             
                          step_result = dispatch_step(scope, step)
         | 
| @@ -6,13 +6,12 @@ module Bolt | |
| 6 6 | 
             
              class PAL
         | 
| 7 7 | 
             
                class YamlPlan
         | 
| 8 8 | 
             
                  class Step
         | 
| 9 | 
            -
                    attr_reader :name, :type, :body, : | 
| 9 | 
            +
                    attr_reader :name, :type, :body, :targets
         | 
| 10 10 |  | 
| 11 11 | 
             
                    def self.allowed_keys
         | 
| 12 | 
            -
                      Set['name', 'description', 'target']
         | 
| 12 | 
            +
                      Set['name', 'description', 'target', 'targets']
         | 
| 13 13 | 
             
                    end
         | 
| 14 14 |  | 
| 15 | 
            -
                    COMMON_STEP_KEYS = %w[name description target].freeze
         | 
| 16 15 | 
             
                    STEP_KEYS = %w[command script task plan source destination eval resources].freeze
         | 
| 17 16 |  | 
| 18 17 | 
             
                    def self.create(step_body, step_number)
         | 
| @@ -38,7 +37,7 @@ module Bolt | |
| 38 37 | 
             
                    def initialize(step_body)
         | 
| 39 38 | 
             
                      @name = step_body['name']
         | 
| 40 39 | 
             
                      @description = step_body['description']
         | 
| 41 | 
            -
                      @ | 
| 40 | 
            +
                      @targets = step_body['targets'] || step_body['target']
         | 
| 42 41 | 
             
                      @body = step_body
         | 
| 43 42 | 
             
                    end
         | 
| 44 43 |  | 
| @@ -82,6 +81,14 @@ module Bolt | |
| 82 81 |  | 
| 83 82 | 
             
                      # Ensure all required keys are present
         | 
| 84 83 | 
             
                      missing_keys = required_keys - body.keys
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                      # Handle cases where steps with a required 'targets' key are using the deprecated
         | 
| 86 | 
            +
                      # 'target' key instead.
         | 
| 87 | 
            +
                      # TODO: Remove this when 'target' is removed
         | 
| 88 | 
            +
                      if body.include?('target')
         | 
| 89 | 
            +
                        missing_keys -= ['targets']
         | 
| 90 | 
            +
                      end
         | 
| 91 | 
            +
             | 
| 85 92 | 
             
                      if missing_keys.any?
         | 
| 86 93 | 
             
                        error_message = "The #{step_type.inspect} step requires: #{missing_keys.to_a.inspect} key(s)"
         | 
| 87 94 | 
             
                        err = step_error(error_message, body['name'], step_number)
         | 
| @@ -10,7 +10,7 @@ module Bolt | |
| 10 10 | 
             
                      end
         | 
| 11 11 |  | 
| 12 12 | 
             
                      def self.required_keys
         | 
| 13 | 
            -
                        Set[' | 
| 13 | 
            +
                        Set['targets']
         | 
| 14 14 | 
             
                      end
         | 
| 15 15 |  | 
| 16 16 | 
             
                      def initialize(step_body)
         | 
| @@ -23,7 +23,7 @@ module Bolt | |
| 23 23 | 
             
                        code << "$#{@name} = " if @name
         | 
| 24 24 |  | 
| 25 25 | 
             
                        fn = 'run_command'
         | 
| 26 | 
            -
                        args = [@command, @ | 
| 26 | 
            +
                        args = [@command, @targets]
         | 
| 27 27 | 
             
                        args << @description if @description
         | 
| 28 28 |  | 
| 29 29 | 
             
                        code << function_call(fn, args)
         | 
| @@ -10,7 +10,7 @@ module Bolt | |
| 10 10 | 
             
                      end
         | 
| 11 11 |  | 
| 12 12 | 
             
                      def self.required_keys
         | 
| 13 | 
            -
                        Set[' | 
| 13 | 
            +
                        Set['targets']
         | 
| 14 14 | 
             
                      end
         | 
| 15 15 |  | 
| 16 16 | 
             
                      def initialize(step_body)
         | 
| @@ -68,14 +68,14 @@ module Bolt | |
| 68 68 |  | 
| 69 69 | 
             
                        code.print "  "
         | 
| 70 70 | 
             
                        fn = 'apply_prep'
         | 
| 71 | 
            -
                        args = [@ | 
| 71 | 
            +
                        args = [@targets]
         | 
| 72 72 | 
             
                        code << function_call(fn, args)
         | 
| 73 73 | 
             
                        code.print "\n"
         | 
| 74 74 |  | 
| 75 75 | 
             
                        code.print "  "
         | 
| 76 76 | 
             
                        code.print "$#{@name} = " if @name
         | 
| 77 77 |  | 
| 78 | 
            -
                        code.puts "apply(#{Bolt::Util.to_code(@ | 
| 78 | 
            +
                        code.puts "apply(#{Bolt::Util.to_code(@targets)}) {"
         | 
| 79 79 |  | 
| 80 80 | 
             
                        declarations = @normalized_resources.map do |resource|
         | 
| 81 81 | 
             
                          type = resource['type'].is_a?(EvaluableString) ? resource['type'].value : resource['type']
         | 
| @@ -10,7 +10,7 @@ module Bolt | |
| 10 10 | 
             
                      end
         | 
| 11 11 |  | 
| 12 12 | 
             
                      def self.required_keys
         | 
| 13 | 
            -
                        Set[' | 
| 13 | 
            +
                        Set['targets']
         | 
| 14 14 | 
             
                      end
         | 
| 15 15 |  | 
| 16 16 | 
             
                      def initialize(step_body)
         | 
| @@ -28,7 +28,7 @@ module Bolt | |
| 28 28 | 
             
                        options['arguments'] = @arguments unless @arguments.empty?
         | 
| 29 29 |  | 
| 30 30 | 
             
                        fn = 'run_script'
         | 
| 31 | 
            -
                        args = [@script, @ | 
| 31 | 
            +
                        args = [@script, @targets]
         | 
| 32 32 | 
             
                        args << @description if @description
         | 
| 33 33 | 
             
                        args << options unless options.empty?
         | 
| 34 34 |  | 
| @@ -10,7 +10,7 @@ module Bolt | |
| 10 10 | 
             
                      end
         | 
| 11 11 |  | 
| 12 12 | 
             
                      def self.required_keys
         | 
| 13 | 
            -
                        Set[' | 
| 13 | 
            +
                        Set['targets']
         | 
| 14 14 | 
             
                      end
         | 
| 15 15 |  | 
| 16 16 | 
             
                      def initialize(step_body)
         | 
| @@ -24,7 +24,7 @@ module Bolt | |
| 24 24 | 
             
                        code << "$#{@name} = " if @name
         | 
| 25 25 |  | 
| 26 26 | 
             
                        fn = 'run_task'
         | 
| 27 | 
            -
                        args = [@task, @ | 
| 27 | 
            +
                        args = [@task, @targets]
         | 
| 28 28 | 
             
                        args << @description if @description
         | 
| 29 29 | 
             
                        args << @parameters unless @parameters.empty?
         | 
| 30 30 |  | 
| @@ -10,7 +10,7 @@ module Bolt | |
| 10 10 | 
             
                      end
         | 
| 11 11 |  | 
| 12 12 | 
             
                      def self.required_keys
         | 
| 13 | 
            -
                        Set[' | 
| 13 | 
            +
                        Set['destination', 'source', 'targets']
         | 
| 14 14 | 
             
                      end
         | 
| 15 15 |  | 
| 16 16 | 
             
                      def initialize(step_body)
         | 
| @@ -24,7 +24,7 @@ module Bolt | |
| 24 24 | 
             
                        code << "$#{@name} = " if @name
         | 
| 25 25 |  | 
| 26 26 | 
             
                        fn = 'upload_file'
         | 
| 27 | 
            -
                        args = [@source, @destination, @ | 
| 27 | 
            +
                        args = [@source, @destination, @targets]
         | 
| 28 28 | 
             
                        args << @description if @description
         | 
| 29 29 |  | 
| 30 30 | 
             
                        code << function_call(fn, args)
         | 
    
        data/lib/bolt/plugin.rb
    CHANGED
    
    | @@ -143,7 +143,8 @@ module Bolt | |
| 143 143 | 
             
                end
         | 
| 144 144 |  | 
| 145 145 | 
             
                RUBY_PLUGINS = %w[task pkcs7 prompt env_var].freeze
         | 
| 146 | 
            -
                BUILTIN_PLUGINS = %w[task terraform pkcs7 prompt vault aws_inventory puppetdb azure_inventory | 
| 146 | 
            +
                BUILTIN_PLUGINS = %w[task terraform pkcs7 prompt vault aws_inventory puppetdb azure_inventory
         | 
| 147 | 
            +
                                     yaml env_var gcloud_inventory].freeze
         | 
| 147 148 | 
             
                DEFAULT_PLUGIN_HOOKS = { 'puppet_library' => { 'plugin' => 'puppet_agent', 'stop_service' => true } }.freeze
         | 
| 148 149 |  | 
| 149 150 | 
             
                attr_reader :pal, :plugin_context
         | 
    
        data/lib/bolt/shell.rb
    CHANGED
    
    
| @@ -0,0 +1,263 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bolt/shell/powershell/snippets'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Bolt
         | 
| 6 | 
            +
              class Shell
         | 
| 7 | 
            +
                class Powershell < Shell
         | 
| 8 | 
            +
                  DEFAULT_EXTENSIONS = Set.new(%w[.ps1 .rb .pp])
         | 
| 9 | 
            +
                  PS_ARGS = %w[-NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass -File].freeze
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def initialize(target, conn)
         | 
| 12 | 
            +
                    super
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    extensions = [target.options['extensions'] || []].flatten.map { |ext| ext[0] != '.' ? '.' + ext : ext }
         | 
| 15 | 
            +
                    extensions += target.options['interpreters'].keys if target.options['interpreters']
         | 
| 16 | 
            +
                    @extensions = DEFAULT_EXTENSIONS + extensions
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def provided_features
         | 
| 20 | 
            +
                    ['powershell']
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def default_input_method(executable)
         | 
| 24 | 
            +
                    powershell_file?(executable) ? 'powershell' : 'both'
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def powershell_file?(path)
         | 
| 28 | 
            +
                    File.extname(path).downcase == '.ps1'
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def validate_extensions(ext)
         | 
| 32 | 
            +
                    unless @extensions.include?(ext)
         | 
| 33 | 
            +
                      raise Bolt::Node::FileError.new("File extension #{ext} is not enabled, "\
         | 
| 34 | 
            +
                                                      "to run it please add to 'winrm: extensions'", 'FILETYPE_ERROR')
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def process_from_extension(path)
         | 
| 39 | 
            +
                    case Pathname(path).extname.downcase
         | 
| 40 | 
            +
                    when '.rb'
         | 
| 41 | 
            +
                      [
         | 
| 42 | 
            +
                        'ruby.exe',
         | 
| 43 | 
            +
                        %W[-S "#{path}"]
         | 
| 44 | 
            +
                      ]
         | 
| 45 | 
            +
                    when '.ps1'
         | 
| 46 | 
            +
                      [
         | 
| 47 | 
            +
                        'powershell.exe',
         | 
| 48 | 
            +
                        [*PS_ARGS, path]
         | 
| 49 | 
            +
                      ]
         | 
| 50 | 
            +
                    when '.pp'
         | 
| 51 | 
            +
                      [
         | 
| 52 | 
            +
                        'puppet.bat',
         | 
| 53 | 
            +
                        %W[apply "#{path}"]
         | 
| 54 | 
            +
                      ]
         | 
| 55 | 
            +
                    else
         | 
| 56 | 
            +
                      # Run the script via cmd, letting Windows extension handling determine how
         | 
| 57 | 
            +
                      [
         | 
| 58 | 
            +
                        'cmd.exe',
         | 
| 59 | 
            +
                        %W[/c "#{path}"]
         | 
| 60 | 
            +
                      ]
         | 
| 61 | 
            +
                    end
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  def escape_arguments(arguments)
         | 
| 65 | 
            +
                    arguments.map do |arg|
         | 
| 66 | 
            +
                      if arg =~ / /
         | 
| 67 | 
            +
                        "\"#{arg}\""
         | 
| 68 | 
            +
                      else
         | 
| 69 | 
            +
                        arg
         | 
| 70 | 
            +
                      end
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  def set_env(arg, val)
         | 
| 75 | 
            +
                    "[Environment]::SetEnvironmentVariable('#{arg}', @'\n#{val}\n'@)"
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  def quote_string(string)
         | 
| 79 | 
            +
                    "'" + string.gsub("'", "''") + "'"
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  def write_executable(dir, file, filename = nil)
         | 
| 83 | 
            +
                    filename ||= File.basename(file)
         | 
| 84 | 
            +
                    validate_extensions(File.extname(filename))
         | 
| 85 | 
            +
                    destination = "#{dir}\\#{filename}"
         | 
| 86 | 
            +
                    conn.copy_file(file, destination)
         | 
| 87 | 
            +
                    destination
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  def execute_process(path, arguments, stdin = nil)
         | 
| 91 | 
            +
                    quoted_args = arguments.map { |arg| quote_string(arg) }.join(' ')
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    quoted_path = if path =~ /^'.*'$/ || path =~ /^".*"$/
         | 
| 94 | 
            +
                                    path
         | 
| 95 | 
            +
                                  else
         | 
| 96 | 
            +
                                    quote_string(path)
         | 
| 97 | 
            +
                                  end
         | 
| 98 | 
            +
                    exec_cmd =
         | 
| 99 | 
            +
                      if stdin.nil?
         | 
| 100 | 
            +
                        "& #{quoted_path} #{quoted_args}"
         | 
| 101 | 
            +
                      else
         | 
| 102 | 
            +
                        <<~STR
         | 
| 103 | 
            +
                        $command_stdin = @'
         | 
| 104 | 
            +
                        #{stdin}
         | 
| 105 | 
            +
                        '@
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                        $command_stdin | & #{quoted_path} #{quoted_args}
         | 
| 108 | 
            +
                        STR
         | 
| 109 | 
            +
                      end
         | 
| 110 | 
            +
                    Snippets.execute_process(exec_cmd)
         | 
| 111 | 
            +
                  end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                  def mkdirs(dirs)
         | 
| 114 | 
            +
                    mkdir_command = "mkdir -Force #{dirs.uniq.sort.join(',')}"
         | 
| 115 | 
            +
                    result = execute(mkdir_command)
         | 
| 116 | 
            +
                    if result.exit_code != 0
         | 
| 117 | 
            +
                      message = "Could not create directories: #{result.stderr.string}"
         | 
| 118 | 
            +
                      raise Bolt::Node::FileError.new(message, 'MKDIR_ERROR')
         | 
| 119 | 
            +
                    end
         | 
| 120 | 
            +
                  end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                  def make_tempdir
         | 
| 123 | 
            +
                    find_parent = target.options['tmpdir'] ? "\"#{target.options['tmpdir']}\"" : '[System.IO.Path]::GetTempPath()'
         | 
| 124 | 
            +
                    result = execute(Snippets.make_tempdir(find_parent))
         | 
| 125 | 
            +
                    if result.exit_code != 0
         | 
| 126 | 
            +
                      raise Bolt::Node::FileError.new("Could not make tempdir: #{result.stderr.string}", 'TEMPDIR_ERROR')
         | 
| 127 | 
            +
                    end
         | 
| 128 | 
            +
                    result.stdout.string.chomp
         | 
| 129 | 
            +
                  end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                  def rmdir(dir)
         | 
| 132 | 
            +
                    execute(Snippets.rmdir(dir))
         | 
| 133 | 
            +
                  end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                  def with_tempdir
         | 
| 136 | 
            +
                    dir = make_tempdir
         | 
| 137 | 
            +
                    yield dir
         | 
| 138 | 
            +
                  ensure
         | 
| 139 | 
            +
                    rmdir(dir)
         | 
| 140 | 
            +
                  end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                  def run_ps_task(task_path, arguments, input_method)
         | 
| 143 | 
            +
                    # NOTE: cannot redirect STDIN to a .ps1 script inside of PowerShell
         | 
| 144 | 
            +
                    # must create new powershell.exe process like other interpreters
         | 
| 145 | 
            +
                    # fortunately, using PS with stdin input_method should never happen
         | 
| 146 | 
            +
                    if input_method == 'powershell'
         | 
| 147 | 
            +
                      Snippets.ps_task(task_path, arguments)
         | 
| 148 | 
            +
                    else
         | 
| 149 | 
            +
                      Snippets.try_catch(task_path)
         | 
| 150 | 
            +
                    end
         | 
| 151 | 
            +
                  end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                  def upload(source, destination, _options = {})
         | 
| 154 | 
            +
                    conn.copy_file(source, destination)
         | 
| 155 | 
            +
                    Bolt::Result.for_upload(target, source, destination)
         | 
| 156 | 
            +
                  end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                  def run_command(command, _options = {})
         | 
| 159 | 
            +
                    output = execute(command)
         | 
| 160 | 
            +
                    Bolt::Result.for_command(target,
         | 
| 161 | 
            +
                                             output.stdout.string,
         | 
| 162 | 
            +
                                             output.stderr.string,
         | 
| 163 | 
            +
                                             output.exit_code,
         | 
| 164 | 
            +
                                             'command', command)
         | 
| 165 | 
            +
                  end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                  def run_script(script, arguments, _options = {})
         | 
| 168 | 
            +
                    # unpack any Sensitive data
         | 
| 169 | 
            +
                    arguments = unwrap_sensitive_args(arguments)
         | 
| 170 | 
            +
                    with_tempdir do |dir|
         | 
| 171 | 
            +
                      script_path = write_executable(dir, script)
         | 
| 172 | 
            +
                      command = if powershell_file?(script_path)
         | 
| 173 | 
            +
                                  Snippets.run_script(arguments, script_path)
         | 
| 174 | 
            +
                                else
         | 
| 175 | 
            +
                                  path, args = *process_from_extension(script_path)
         | 
| 176 | 
            +
                                  args += escape_arguments(arguments)
         | 
| 177 | 
            +
                                  execute_process(path, args)
         | 
| 178 | 
            +
                                end
         | 
| 179 | 
            +
                      output = execute(command)
         | 
| 180 | 
            +
                      Bolt::Result.for_command(target,
         | 
| 181 | 
            +
                                               output.stdout.string,
         | 
| 182 | 
            +
                                               output.stderr.string,
         | 
| 183 | 
            +
                                               output.exit_code,
         | 
| 184 | 
            +
                                               'script', script)
         | 
| 185 | 
            +
                    end
         | 
| 186 | 
            +
                  end
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                  def run_task(task, arguments, _options = {})
         | 
| 189 | 
            +
                    implementation = select_implementation(target, task)
         | 
| 190 | 
            +
                    executable = implementation['path']
         | 
| 191 | 
            +
                    input_method = implementation['input_method']
         | 
| 192 | 
            +
                    extra_files = implementation['files']
         | 
| 193 | 
            +
                    input_method ||= powershell_file?(executable) ? 'powershell' : 'both'
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                    # unpack any Sensitive data
         | 
| 196 | 
            +
                    arguments = unwrap_sensitive_args(arguments)
         | 
| 197 | 
            +
                    with_tempdir do |dir|
         | 
| 198 | 
            +
                      if extra_files.empty?
         | 
| 199 | 
            +
                        task_dir = dir
         | 
| 200 | 
            +
                      else
         | 
| 201 | 
            +
                        # TODO: optimize upload of directories
         | 
| 202 | 
            +
                        arguments['_installdir'] = dir
         | 
| 203 | 
            +
                        task_dir = File.join(dir, task.tasks_dir)
         | 
| 204 | 
            +
                        mkdirs([task_dir] + extra_files.map { |file| File.join(dir, File.dirname(file['name'])) })
         | 
| 205 | 
            +
                        extra_files.each do |file|
         | 
| 206 | 
            +
                          conn.copy_file(file['path'], File.join(dir, file['name']))
         | 
| 207 | 
            +
                        end
         | 
| 208 | 
            +
                      end
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                      task_path = write_executable(task_dir, executable)
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                      if Bolt::Task::STDIN_METHODS.include?(input_method)
         | 
| 213 | 
            +
                        stdin = JSON.dump(arguments)
         | 
| 214 | 
            +
                      end
         | 
| 215 | 
            +
             | 
| 216 | 
            +
                      command = if powershell_file?(task_path) && stdin.nil?
         | 
| 217 | 
            +
                                  run_ps_task(task_path, arguments, input_method)
         | 
| 218 | 
            +
                                else
         | 
| 219 | 
            +
                                  if (interpreter = select_interpreter(task_path, target.options['interpreters']))
         | 
| 220 | 
            +
                                    path = interpreter
         | 
| 221 | 
            +
                                    args = [task_path]
         | 
| 222 | 
            +
                                  else
         | 
| 223 | 
            +
                                    path, args = *process_from_extension(task_path)
         | 
| 224 | 
            +
                                  end
         | 
| 225 | 
            +
                                  execute_process(path, args, stdin)
         | 
| 226 | 
            +
                                end
         | 
| 227 | 
            +
             | 
| 228 | 
            +
                      env_assignments = if Bolt::Task::ENVIRONMENT_METHODS.include?(input_method)
         | 
| 229 | 
            +
                                          envify_params(arguments).map do |(arg, val)|
         | 
| 230 | 
            +
                                            set_env(arg, val)
         | 
| 231 | 
            +
                                          end
         | 
| 232 | 
            +
                                        else
         | 
| 233 | 
            +
                                          []
         | 
| 234 | 
            +
                                        end
         | 
| 235 | 
            +
             | 
| 236 | 
            +
                      output = execute([Snippets.shell_init, *env_assignments, command].join("\n"))
         | 
| 237 | 
            +
             | 
| 238 | 
            +
                      Bolt::Result.for_task(target, output.stdout.string,
         | 
| 239 | 
            +
                                            output.stderr.string,
         | 
| 240 | 
            +
                                            output.exit_code,
         | 
| 241 | 
            +
                                            task.name)
         | 
| 242 | 
            +
                    end
         | 
| 243 | 
            +
                  end
         | 
| 244 | 
            +
             | 
| 245 | 
            +
                  def execute(command)
         | 
| 246 | 
            +
                    inp, out, err, t = conn.execute(command)
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                    result = Bolt::Node::Output.new
         | 
| 249 | 
            +
                    inp.close
         | 
| 250 | 
            +
                    out.binmode
         | 
| 251 | 
            +
                    err.binmode
         | 
| 252 | 
            +
                    stdout = Thread.new { result.stdout << out.read }
         | 
| 253 | 
            +
                    stderr = Thread.new { result.stderr << err.read }
         | 
| 254 | 
            +
             | 
| 255 | 
            +
                    stdout.join
         | 
| 256 | 
            +
                    stderr.join
         | 
| 257 | 
            +
                    result.exit_code = t.value.respond_to?(:exitstatus) ? t.value.exitstatus : t.value
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                    result
         | 
| 260 | 
            +
                  end
         | 
| 261 | 
            +
                end
         | 
| 262 | 
            +
              end
         | 
| 263 | 
            +
            end
         |