bolt 1.5.0 → 1.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/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +1 -1
- data/lib/bolt/config.rb +14 -10
- data/lib/bolt/executor.rb +10 -4
- data/lib/bolt/inventory.rb +11 -10
- data/lib/bolt/node/errors.rb +8 -1
- data/lib/bolt/target.rb +25 -2
- data/lib/bolt/task.rb +13 -2
- data/lib/bolt/task/remote.rb +25 -0
- data/lib/bolt/transport/base.rb +22 -6
- data/lib/bolt/transport/docker.rb +6 -0
- data/lib/bolt/transport/local.rb +64 -20
- data/lib/bolt/transport/powershell.rb +330 -0
- data/lib/bolt/transport/remote.rb +53 -0
- data/lib/bolt/transport/ssh.rb +1 -2
- data/lib/bolt/transport/winrm.rb +16 -89
- data/lib/bolt/transport/winrm/connection.rb +5 -216
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/file_cache.rb +1 -1
- metadata +19 -2
| @@ -0,0 +1,330 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Bolt
         | 
| 4 | 
            +
              module Transport
         | 
| 5 | 
            +
                module Powershell
         | 
| 6 | 
            +
                  class << self
         | 
| 7 | 
            +
                    PS_ARGS = %w[
         | 
| 8 | 
            +
                      -NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass
         | 
| 9 | 
            +
                    ].freeze
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    def powershell_file?(path)
         | 
| 12 | 
            +
                      Pathname(path).extname.casecmp('.ps1').zero?
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    def process_from_extension(path)
         | 
| 16 | 
            +
                      case Pathname(path).extname.downcase
         | 
| 17 | 
            +
                      when '.rb'
         | 
| 18 | 
            +
                        [
         | 
| 19 | 
            +
                          'ruby.exe',
         | 
| 20 | 
            +
                          ['-S', "\"#{path}\""]
         | 
| 21 | 
            +
                        ]
         | 
| 22 | 
            +
                      when '.ps1'
         | 
| 23 | 
            +
                        [
         | 
| 24 | 
            +
                          'powershell.exe',
         | 
| 25 | 
            +
                          [*PS_ARGS, '-File', "\"#{path}\""]
         | 
| 26 | 
            +
                        ]
         | 
| 27 | 
            +
                      when '.pp'
         | 
| 28 | 
            +
                        [
         | 
| 29 | 
            +
                          'puppet.bat',
         | 
| 30 | 
            +
                          ['apply', "\"#{path}\""]
         | 
| 31 | 
            +
                        ]
         | 
| 32 | 
            +
                      else
         | 
| 33 | 
            +
                        # Run the script via cmd, letting Windows extension handling determine how
         | 
| 34 | 
            +
                        [
         | 
| 35 | 
            +
                          'cmd.exe',
         | 
| 36 | 
            +
                          ['/c', "\"#{path}\""]
         | 
| 37 | 
            +
                        ]
         | 
| 38 | 
            +
                      end
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    def escape_arguments(arguments)
         | 
| 42 | 
            +
                      arguments.map do |arg|
         | 
| 43 | 
            +
                        if arg =~ / /
         | 
| 44 | 
            +
                          "\"#{arg}\""
         | 
| 45 | 
            +
                        else
         | 
| 46 | 
            +
                          arg
         | 
| 47 | 
            +
                        end
         | 
| 48 | 
            +
                      end
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    def set_env(arg, val)
         | 
| 52 | 
            +
                      "[Environment]::SetEnvironmentVariable('#{arg}', @'\n#{val}\n'@)"
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    def execute_process(path, arguments, stdin = nil)
         | 
| 56 | 
            +
                      quoted_args = arguments.map do |arg|
         | 
| 57 | 
            +
                        "'" + arg.gsub("'", "''") + "'"
         | 
| 58 | 
            +
                      end.join(' ')
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                      exec_cmd =
         | 
| 61 | 
            +
                        if stdin.nil?
         | 
| 62 | 
            +
                          "& #{path} #{quoted_args}"
         | 
| 63 | 
            +
                        else
         | 
| 64 | 
            +
                          "@'\n#{stdin}\n'@ | & #{path} #{quoted_args}"
         | 
| 65 | 
            +
                        end
         | 
| 66 | 
            +
                      <<-PS
         | 
| 67 | 
            +
            $OutputEncoding = [Console]::OutputEncoding
         | 
| 68 | 
            +
            #{exec_cmd}
         | 
| 69 | 
            +
            if (-not $? -and ($LASTEXITCODE -eq $null)) { exit 1 }
         | 
| 70 | 
            +
            exit $LASTEXITCODE
         | 
| 71 | 
            +
            PS
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    def mkdirs(dirs)
         | 
| 75 | 
            +
                      "mkdir -Force #{dirs.uniq.sort.join(',')}"
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                    def make_tempdir(parent)
         | 
| 79 | 
            +
                      <<-PS
         | 
| 80 | 
            +
            $parent = #{parent}
         | 
| 81 | 
            +
            $name = [System.IO.Path]::GetRandomFileName()
         | 
| 82 | 
            +
            $path = Join-Path $parent $name
         | 
| 83 | 
            +
            New-Item -ItemType Directory -Path $path | Out-Null
         | 
| 84 | 
            +
            $path
         | 
| 85 | 
            +
            PS
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                    def rmdir(dir)
         | 
| 89 | 
            +
                      <<-PS
         | 
| 90 | 
            +
            Remove-Item -Force -Recurse -Path "#{dir}"
         | 
| 91 | 
            +
            PS
         | 
| 92 | 
            +
                    end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    def run_script(arguments, script_path)
         | 
| 95 | 
            +
                      mapped_args = arguments.map do |a|
         | 
| 96 | 
            +
                        "$invokeArgs.ArgumentList += @'\n#{a}\n'@"
         | 
| 97 | 
            +
                      end.join("\n")
         | 
| 98 | 
            +
                      <<-PS
         | 
| 99 | 
            +
            $invokeArgs = @{
         | 
| 100 | 
            +
              ScriptBlock = (Get-Command "#{script_path}").ScriptBlock
         | 
| 101 | 
            +
              ArgumentList = @()
         | 
| 102 | 
            +
            }
         | 
| 103 | 
            +
            #{mapped_args}
         | 
| 104 | 
            +
             | 
| 105 | 
            +
            try
         | 
| 106 | 
            +
            {
         | 
| 107 | 
            +
              Invoke-Command @invokeArgs
         | 
| 108 | 
            +
            }
         | 
| 109 | 
            +
            catch
         | 
| 110 | 
            +
            {
         | 
| 111 | 
            +
              Write-Error $_.Exception
         | 
| 112 | 
            +
              exit 1
         | 
| 113 | 
            +
            }
         | 
| 114 | 
            +
            PS
         | 
| 115 | 
            +
                    end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                    def run_ps_task(arguments, task_path, input_method)
         | 
| 118 | 
            +
                      # NOTE: cannot redirect STDIN to a .ps1 script inside of PowerShell
         | 
| 119 | 
            +
                      # must create new powershell.exe process like other interpreters
         | 
| 120 | 
            +
                      # fortunately, using PS with stdin input_method should never happen
         | 
| 121 | 
            +
                      if input_method == 'powershell'
         | 
| 122 | 
            +
                        <<-PS
         | 
| 123 | 
            +
            $private:tempArgs = Get-ContentAsJson (
         | 
| 124 | 
            +
              $utf8.GetString([System.Convert]::FromBase64String('#{Base64.encode64(JSON.dump(arguments))}'))
         | 
| 125 | 
            +
            )
         | 
| 126 | 
            +
            $allowedArgs = (Get-Command "#{task_path}").Parameters.Keys
         | 
| 127 | 
            +
            $private:taskArgs = @{}
         | 
| 128 | 
            +
            $private:tempArgs.Keys | ? { $allowedArgs -contains $_ } | % { $private:taskArgs[$_] = $private:tempArgs[$_] }
         | 
| 129 | 
            +
            try { & "#{task_path}" @taskArgs } catch { Write-Error $_.Exception; exit 1 }
         | 
| 130 | 
            +
            PS
         | 
| 131 | 
            +
                      else
         | 
| 132 | 
            +
                        %(try { & "#{task_path}" } catch { Write-Error $_.Exception; exit 1 })
         | 
| 133 | 
            +
                      end
         | 
| 134 | 
            +
                    end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                    def shell_init
         | 
| 137 | 
            +
                      <<-PS
         | 
| 138 | 
            +
            $ENV:PATH += ";${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\bin\\;" +
         | 
| 139 | 
            +
            "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\puppet\\bin;" +
         | 
| 140 | 
            +
            "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\sys\\ruby\\bin\\"
         | 
| 141 | 
            +
            $ENV:RUBYLIB = "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\puppet\\lib;" +
         | 
| 142 | 
            +
            "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\facter\\lib;" +
         | 
| 143 | 
            +
            "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\hiera\\lib;" +
         | 
| 144 | 
            +
            $ENV:RUBYLIB
         | 
| 145 | 
            +
             | 
| 146 | 
            +
            Add-Type -AssemblyName System.ServiceModel.Web, System.Runtime.Serialization
         | 
| 147 | 
            +
            $utf8 = [System.Text.Encoding]::UTF8
         | 
| 148 | 
            +
             | 
| 149 | 
            +
            function Write-Stream {
         | 
| 150 | 
            +
            PARAM(
         | 
| 151 | 
            +
              [Parameter(Position=0)] $stream,
         | 
| 152 | 
            +
              [Parameter(ValueFromPipeline=$true)] $string
         | 
| 153 | 
            +
            )
         | 
| 154 | 
            +
            PROCESS {
         | 
| 155 | 
            +
              $bytes = $utf8.GetBytes($string)
         | 
| 156 | 
            +
              $stream.Write( $bytes, 0, $bytes.Length )
         | 
| 157 | 
            +
            }
         | 
| 158 | 
            +
            }
         | 
| 159 | 
            +
             | 
| 160 | 
            +
            function Convert-JsonToXml {
         | 
| 161 | 
            +
            PARAM([Parameter(ValueFromPipeline=$true)] [string[]] $json)
         | 
| 162 | 
            +
            BEGIN {
         | 
| 163 | 
            +
              $mStream = New-Object System.IO.MemoryStream
         | 
| 164 | 
            +
            }
         | 
| 165 | 
            +
            PROCESS {
         | 
| 166 | 
            +
              $json | Write-Stream -Stream $mStream
         | 
| 167 | 
            +
            }
         | 
| 168 | 
            +
            END {
         | 
| 169 | 
            +
              $mStream.Position = 0
         | 
| 170 | 
            +
              try {
         | 
| 171 | 
            +
                $jsonReader = [System.Runtime.Serialization.Json.JsonReaderWriterFactory]::CreateJsonReader($mStream,[System.Xml.XmlDictionaryReaderQuotas]::Max)
         | 
| 172 | 
            +
                $xml = New-Object Xml.XmlDocument
         | 
| 173 | 
            +
                $xml.Load($jsonReader)
         | 
| 174 | 
            +
                $xml
         | 
| 175 | 
            +
              } finally {
         | 
| 176 | 
            +
                $jsonReader.Close()
         | 
| 177 | 
            +
                $mStream.Dispose()
         | 
| 178 | 
            +
              }
         | 
| 179 | 
            +
            }
         | 
| 180 | 
            +
            }
         | 
| 181 | 
            +
             | 
| 182 | 
            +
            Function ConvertFrom-Xml {
         | 
| 183 | 
            +
            [CmdletBinding(DefaultParameterSetName="AutoType")]
         | 
| 184 | 
            +
            PARAM(
         | 
| 185 | 
            +
              [Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [Xml.XmlNode] $xml,
         | 
| 186 | 
            +
              [Parameter(Mandatory=$true,ParameterSetName="ManualType")] [Type] $Type,
         | 
| 187 | 
            +
              [Switch] $ForceType
         | 
| 188 | 
            +
            )
         | 
| 189 | 
            +
            PROCESS{
         | 
| 190 | 
            +
              if (Get-Member -InputObject $xml -Name root) {
         | 
| 191 | 
            +
                return $xml.root.Objects | ConvertFrom-Xml
         | 
| 192 | 
            +
              } elseif (Get-Member -InputObject $xml -Name Objects) {
         | 
| 193 | 
            +
                return $xml.Objects | ConvertFrom-Xml
         | 
| 194 | 
            +
              }
         | 
| 195 | 
            +
              $propbag = @{}
         | 
| 196 | 
            +
              foreach ($name in Get-Member -InputObject $xml -MemberType Properties | Where-Object{$_.Name -notmatch "^__|type"} | Select-Object -ExpandProperty name) {
         | 
| 197 | 
            +
                Write-Debug "$Name Type: $($xml.$Name.type)" -Debug:$false
         | 
| 198 | 
            +
                $propbag."$Name" = Convert-Properties $xml."$name"
         | 
| 199 | 
            +
              }
         | 
| 200 | 
            +
              if (!$Type -and $xml.HasAttribute("__type")) { $Type = $xml.__Type }
         | 
| 201 | 
            +
              if ($ForceType -and $Type) {
         | 
| 202 | 
            +
                try {
         | 
| 203 | 
            +
                  $output = New-Object $Type -Property $propbag
         | 
| 204 | 
            +
                } catch {
         | 
| 205 | 
            +
                  $output = New-Object PSObject -Property $propbag
         | 
| 206 | 
            +
                  $output.PsTypeNames.Insert(0, $xml.__type)
         | 
| 207 | 
            +
                }
         | 
| 208 | 
            +
              } elseif ($propbag.Count -ne 0) {
         | 
| 209 | 
            +
                $output = New-Object PSObject -Property $propbag
         | 
| 210 | 
            +
                if ($Type) {
         | 
| 211 | 
            +
                  $output.PsTypeNames.Insert(0, $Type)
         | 
| 212 | 
            +
                }
         | 
| 213 | 
            +
              }
         | 
| 214 | 
            +
              return $output
         | 
| 215 | 
            +
            }
         | 
| 216 | 
            +
            }
         | 
| 217 | 
            +
             | 
| 218 | 
            +
            Function Convert-Properties {
         | 
| 219 | 
            +
            PARAM($InputObject)
         | 
| 220 | 
            +
            switch ($InputObject.type) {
         | 
| 221 | 
            +
              "object" {
         | 
| 222 | 
            +
                return (ConvertFrom-Xml -Xml $InputObject)
         | 
| 223 | 
            +
              }
         | 
| 224 | 
            +
              "string" {
         | 
| 225 | 
            +
                $MightBeADate = $InputObject.get_InnerText() -as [DateTime]
         | 
| 226 | 
            +
                ## Strings that are actually dates (*grumble* JSON is crap)
         | 
| 227 | 
            +
                if ($MightBeADate -and $propbag."$Name" -eq $MightBeADate.ToString("G")) {
         | 
| 228 | 
            +
                  return $MightBeADate
         | 
| 229 | 
            +
                } else {
         | 
| 230 | 
            +
                  return $InputObject.get_InnerText()
         | 
| 231 | 
            +
                }
         | 
| 232 | 
            +
              }
         | 
| 233 | 
            +
              "number" {
         | 
| 234 | 
            +
                $number = $InputObject.get_InnerText()
         | 
| 235 | 
            +
                if ($number -eq ($number -as [int])) {
         | 
| 236 | 
            +
                  return $number -as [int]
         | 
| 237 | 
            +
                } elseif ($number -eq ($number -as [double])) {
         | 
| 238 | 
            +
                  return $number -as [double]
         | 
| 239 | 
            +
                } else {
         | 
| 240 | 
            +
                  return $number -as [decimal]
         | 
| 241 | 
            +
                }
         | 
| 242 | 
            +
              }
         | 
| 243 | 
            +
              "boolean" {
         | 
| 244 | 
            +
                return [bool]::parse($InputObject.get_InnerText())
         | 
| 245 | 
            +
              }
         | 
| 246 | 
            +
              "null" {
         | 
| 247 | 
            +
                return $null
         | 
| 248 | 
            +
              }
         | 
| 249 | 
            +
              "array" {
         | 
| 250 | 
            +
                [object[]]$Items = $(foreach( $item in $InputObject.GetEnumerator() ) {
         | 
| 251 | 
            +
                  Convert-Properties $item
         | 
| 252 | 
            +
                })
         | 
| 253 | 
            +
                return $Items
         | 
| 254 | 
            +
              }
         | 
| 255 | 
            +
              default {
         | 
| 256 | 
            +
                return $InputObject
         | 
| 257 | 
            +
              }
         | 
| 258 | 
            +
            }
         | 
| 259 | 
            +
            }
         | 
| 260 | 
            +
             | 
| 261 | 
            +
            Function ConvertFrom-Json2 {
         | 
| 262 | 
            +
            [CmdletBinding()]
         | 
| 263 | 
            +
            PARAM(
         | 
| 264 | 
            +
              [Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [string] $InputObject,
         | 
| 265 | 
            +
              [Parameter(Mandatory=$true)] [Type] $Type,
         | 
| 266 | 
            +
              [Switch] $ForceType
         | 
| 267 | 
            +
            )
         | 
| 268 | 
            +
            PROCESS {
         | 
| 269 | 
            +
              $null = $PSBoundParameters.Remove("InputObject")
         | 
| 270 | 
            +
              [Xml.XmlElement]$xml = (Convert-JsonToXml $InputObject).Root
         | 
| 271 | 
            +
              if ($xml) {
         | 
| 272 | 
            +
                if ($xml.Objects) {
         | 
| 273 | 
            +
                  $xml.Objects.Item.GetEnumerator() | ConvertFrom-Xml @PSBoundParameters
         | 
| 274 | 
            +
                } elseif ($xml.Item -and $xml.Item -isnot [System.Management.Automation.PSParameterizedProperty]) {
         | 
| 275 | 
            +
                  $xml.Item | ConvertFrom-Xml @PSBoundParameters
         | 
| 276 | 
            +
                } else {
         | 
| 277 | 
            +
                  $xml | ConvertFrom-Xml @PSBoundParameters
         | 
| 278 | 
            +
                }
         | 
| 279 | 
            +
              } else {
         | 
| 280 | 
            +
                Write-Error "Failed to parse JSON with JsonReader" -Debug:$false
         | 
| 281 | 
            +
              }
         | 
| 282 | 
            +
            }
         | 
| 283 | 
            +
            }
         | 
| 284 | 
            +
             | 
| 285 | 
            +
            function ConvertFrom-PSCustomObject
         | 
| 286 | 
            +
            {
         | 
| 287 | 
            +
            PARAM([Parameter(ValueFromPipeline = $true)] $InputObject)
         | 
| 288 | 
            +
            PROCESS {
         | 
| 289 | 
            +
              if ($null -eq $InputObject) { return $null }
         | 
| 290 | 
            +
             | 
| 291 | 
            +
              if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]) {
         | 
| 292 | 
            +
                $collection = @(
         | 
| 293 | 
            +
                  foreach ($object in $InputObject) { ConvertFrom-PSCustomObject $object }
         | 
| 294 | 
            +
                )
         | 
| 295 | 
            +
             | 
| 296 | 
            +
                $collection
         | 
| 297 | 
            +
              } elseif ($InputObject -is [System.Management.Automation.PSCustomObject]) {
         | 
| 298 | 
            +
                $hash = @{}
         | 
| 299 | 
            +
                foreach ($property in $InputObject.PSObject.Properties) {
         | 
| 300 | 
            +
                  $hash[$property.Name] = ConvertFrom-PSCustomObject $property.Value
         | 
| 301 | 
            +
                }
         | 
| 302 | 
            +
             | 
| 303 | 
            +
                $hash
         | 
| 304 | 
            +
              } else {
         | 
| 305 | 
            +
                $InputObject
         | 
| 306 | 
            +
              }
         | 
| 307 | 
            +
            }
         | 
| 308 | 
            +
            }
         | 
| 309 | 
            +
             | 
| 310 | 
            +
            function Get-ContentAsJson
         | 
| 311 | 
            +
            {
         | 
| 312 | 
            +
            [CmdletBinding()]
         | 
| 313 | 
            +
            PARAM(
         | 
| 314 | 
            +
              [Parameter(Mandatory = $true)] $Text,
         | 
| 315 | 
            +
              [Parameter(Mandatory = $false)] [Text.Encoding] $Encoding = [Text.Encoding]::UTF8
         | 
| 316 | 
            +
            )
         | 
| 317 | 
            +
             | 
| 318 | 
            +
            # using polyfill cmdlet on PS2, so pass type info
         | 
| 319 | 
            +
            if ($PSVersionTable.PSVersion -lt [Version]'3.0') {
         | 
| 320 | 
            +
              $Text | ConvertFrom-Json2 -Type PSObject | ConvertFrom-PSCustomObject
         | 
| 321 | 
            +
            } else {
         | 
| 322 | 
            +
              $Text | ConvertFrom-Json | ConvertFrom-PSCustomObject
         | 
| 323 | 
            +
            }
         | 
| 324 | 
            +
            }
         | 
| 325 | 
            +
            PS
         | 
| 326 | 
            +
                    end
         | 
| 327 | 
            +
                  end
         | 
| 328 | 
            +
                end
         | 
| 329 | 
            +
              end
         | 
| 330 | 
            +
            end
         | 
| @@ -0,0 +1,53 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bolt/task/remote'
         | 
| 4 | 
            +
            require 'bolt/transport/base'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Bolt
         | 
| 7 | 
            +
              module Transport
         | 
| 8 | 
            +
                class Remote < Base
         | 
| 9 | 
            +
                  # The options for the remote transport not defined.
         | 
| 10 | 
            +
                  def self.filter_options(unfiltered)
         | 
| 11 | 
            +
                    unfiltered
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def self.validate(options)
         | 
| 15 | 
            +
                    # This will fail when validating global config
         | 
| 16 | 
            +
                    # unless options['device-type']
         | 
| 17 | 
            +
                    #  raise Bolt::ValidationError, 'Must specify device-type for devices'
         | 
| 18 | 
            +
                    # end
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  # TODO: this should have access to inventory so target doesn't have to
         | 
| 22 | 
            +
                  def initialize(executor)
         | 
| 23 | 
            +
                    super()
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    @executor = executor
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def get_proxy(target)
         | 
| 29 | 
            +
                    inventory = target.inventory
         | 
| 30 | 
            +
                    raise "Target was created without inventory? Not get_targets?" unless inventory
         | 
| 31 | 
            +
                    proxy = inventory.get_targets(target.options['run-on'] || 'localhost').first
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    if proxy.transport == 'remote'
         | 
| 34 | 
            +
                      msg = "#{proxy.name} is not a valid run-on target for #{target.name} since is also remote."
         | 
| 35 | 
            +
                      raise Bolt::Error.new(msg, 'bolt/invalid-remote-target')
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
                    proxy
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  # Cannot batch because arugments differ
         | 
| 41 | 
            +
                  def run_task(target, task, arguments, options = {})
         | 
| 42 | 
            +
                    proxy_target = get_proxy(target)
         | 
| 43 | 
            +
                    transport = @executor.transport(proxy_target.protocol)
         | 
| 44 | 
            +
                    arguments = arguments.merge('_target' => target.to_h.reject { |_, v| v.nil? })
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    remote_task = Bolt::Task::Remote.new(task.to_h)
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                    result = transport.run_task(proxy_target, remote_task, arguments, options)
         | 
| 49 | 
            +
                    Bolt::Result.new(target, value: result.value)
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
            end
         | 
    
        data/lib/bolt/transport/ssh.rb
    CHANGED
    
    | @@ -121,11 +121,10 @@ module Bolt | |
| 121 121 | 
             
                  end
         | 
| 122 122 |  | 
| 123 123 | 
             
                  def run_task(target, task, arguments, options = {})
         | 
| 124 | 
            -
                    implementation =  | 
| 124 | 
            +
                    implementation = select_implementation(target, task)
         | 
| 125 125 | 
             
                    executable = implementation['path']
         | 
| 126 126 | 
             
                    input_method = implementation['input_method']
         | 
| 127 127 | 
             
                    extra_files = implementation['files']
         | 
| 128 | 
            -
                    input_method ||= 'both'
         | 
| 129 128 |  | 
| 130 129 | 
             
                    # unpack any Sensitive data
         | 
| 131 130 | 
             
                    arguments = unwrap_sensitive_args(arguments)
         | 
    
        data/lib/bolt/transport/winrm.rb
    CHANGED
    
    | @@ -2,14 +2,11 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            require 'bolt/node/errors'
         | 
| 4 4 | 
             
            require 'bolt/transport/base'
         | 
| 5 | 
            +
            require 'bolt/transport/powershell'
         | 
| 5 6 |  | 
| 6 7 | 
             
            module Bolt
         | 
| 7 8 | 
             
              module Transport
         | 
| 8 9 | 
             
                class WinRM < Base
         | 
| 9 | 
            -
                  PS_ARGS = %w[
         | 
| 10 | 
            -
                    -NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass
         | 
| 11 | 
            -
                  ].freeze
         | 
| 12 | 
            -
             | 
| 13 10 | 
             
                  def self.options
         | 
| 14 11 | 
             
                    %w[port user password connect-timeout ssl ssl-verify tmpdir cacert extensions]
         | 
| 15 12 | 
             
                  end
         | 
| @@ -18,6 +15,11 @@ module Bolt | |
| 18 15 | 
             
                    ['powershell']
         | 
| 19 16 | 
             
                  end
         | 
| 20 17 |  | 
| 18 | 
            +
                  def default_input_method(executable)
         | 
| 19 | 
            +
                    input_method ||= Powershell.powershell_file?(executable) ? 'powershell' : 'both'
         | 
| 20 | 
            +
                    input_method
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 21 23 | 
             
                  def self.validate(options)
         | 
| 22 24 | 
             
                    ssl_flag = options['ssl']
         | 
| 23 25 | 
             
                    unless !!ssl_flag == ssl_flag
         | 
| @@ -74,34 +76,14 @@ module Bolt | |
| 74 76 | 
             
                  def run_script(target, script, arguments, _options = {})
         | 
| 75 77 | 
             
                    # unpack any Sensitive data
         | 
| 76 78 | 
             
                    arguments = unwrap_sensitive_args(arguments)
         | 
| 77 | 
            -
             | 
| 78 79 | 
             
                    with_connection(target) do |conn|
         | 
| 79 80 | 
             
                      conn.with_remote_tempdir do |dir|
         | 
| 80 81 | 
             
                        remote_path = conn.write_remote_executable(dir, script)
         | 
| 81 | 
            -
                        if powershell_file?(remote_path)
         | 
| 82 | 
            -
                           | 
| 83 | 
            -
                            "$invokeArgs.ArgumentList += @'\n#{a}\n'@"
         | 
| 84 | 
            -
                          end.join("\n")
         | 
| 85 | 
            -
                          output = conn.execute(<<-PS)
         | 
| 86 | 
            -
            $invokeArgs = @{
         | 
| 87 | 
            -
              ScriptBlock = (Get-Command "#{remote_path}").ScriptBlock
         | 
| 88 | 
            -
              ArgumentList = @()
         | 
| 89 | 
            -
            }
         | 
| 90 | 
            -
            #{mapped_args}
         | 
| 91 | 
            -
             | 
| 92 | 
            -
            try
         | 
| 93 | 
            -
            {
         | 
| 94 | 
            -
              Invoke-Command @invokeArgs
         | 
| 95 | 
            -
            }
         | 
| 96 | 
            -
            catch
         | 
| 97 | 
            -
            {
         | 
| 98 | 
            -
              Write-Error $_.Exception
         | 
| 99 | 
            -
              exit 1
         | 
| 100 | 
            -
            }
         | 
| 101 | 
            -
                      PS
         | 
| 82 | 
            +
                        if Powershell.powershell_file?(remote_path)
         | 
| 83 | 
            +
                          output = conn.execute(Powershell.run_script(arguments, remote_path))
         | 
| 102 84 | 
             
                        else
         | 
| 103 | 
            -
                          path, args = *process_from_extension(remote_path)
         | 
| 104 | 
            -
                          args += escape_arguments(arguments)
         | 
| 85 | 
            +
                          path, args = *Powershell.process_from_extension(remote_path)
         | 
| 86 | 
            +
                          args += Powershell.escape_arguments(arguments)
         | 
| 105 87 | 
             
                          output = conn.execute_process(path, args)
         | 
| 106 88 | 
             
                        end
         | 
| 107 89 | 
             
                        Bolt::Result.for_command(target, output.stdout.string, output.stderr.string, output.exit_code)
         | 
| @@ -110,11 +92,11 @@ catch | |
| 110 92 | 
             
                  end
         | 
| 111 93 |  | 
| 112 94 | 
             
                  def run_task(target, task, arguments, _options = {})
         | 
| 113 | 
            -
                    implementation =  | 
| 95 | 
            +
                    implementation = select_implementation(target, task)
         | 
| 114 96 | 
             
                    executable = implementation['path']
         | 
| 115 97 | 
             
                    input_method = implementation['input_method']
         | 
| 116 98 | 
             
                    extra_files = implementation['files']
         | 
| 117 | 
            -
                    input_method ||= powershell_file?(executable) ? 'powershell' : 'both'
         | 
| 99 | 
            +
                    input_method ||= Powershell.powershell_file?(executable) ? 'powershell' : 'both'
         | 
| 118 100 |  | 
| 119 101 | 
             
                    # unpack any Sensitive data
         | 
| 120 102 | 
             
                    arguments = unwrap_sensitive_args(arguments)
         | 
| @@ -140,7 +122,7 @@ catch | |
| 140 122 |  | 
| 141 123 | 
             
                        if ENVIRONMENT_METHODS.include?(input_method)
         | 
| 142 124 | 
             
                          envify_params(arguments).each do |(arg, val)|
         | 
| 143 | 
            -
                            cmd =  | 
| 125 | 
            +
                            cmd = Powershell.set_env(arg, val)
         | 
| 144 126 | 
             
                            result = conn.execute(cmd)
         | 
| 145 127 | 
             
                            if result.exit_code != 0
         | 
| 146 128 | 
             
                              raise Bolt::Node::EnvironmentVarError.new(arg, val)
         | 
| @@ -150,25 +132,10 @@ catch | |
| 150 132 |  | 
| 151 133 | 
             
                        conn.shell_init
         | 
| 152 134 | 
             
                        output =
         | 
| 153 | 
            -
                          if powershell_file?(remote_task_path) && stdin.nil?
         | 
| 154 | 
            -
                             | 
| 155 | 
            -
                            # must create new powershell.exe process like other interpreters
         | 
| 156 | 
            -
                            # fortunately, using PS with stdin input_method should never happen
         | 
| 157 | 
            -
                            if input_method == 'powershell'
         | 
| 158 | 
            -
                              conn.execute(<<-PS)
         | 
| 159 | 
            -
            $private:tempArgs = Get-ContentAsJson (
         | 
| 160 | 
            -
              $utf8.GetString([System.Convert]::FromBase64String('#{Base64.encode64(JSON.dump(arguments))}'))
         | 
| 161 | 
            -
            )
         | 
| 162 | 
            -
            $allowedArgs = (Get-Command "#{remote_task_path}").Parameters.Keys
         | 
| 163 | 
            -
            $private:taskArgs = @{}
         | 
| 164 | 
            -
            $private:tempArgs.Keys | ? { $allowedArgs -contains $_ } | % { $private:taskArgs[$_] = $private:tempArgs[$_] }
         | 
| 165 | 
            -
            try { & "#{remote_task_path}" @taskArgs } catch { Write-Error $_.Exception; exit 1 }
         | 
| 166 | 
            -
                          PS
         | 
| 167 | 
            -
                            else
         | 
| 168 | 
            -
                              conn.execute(%(try { & "#{remote_task_path}" } catch { Write-Error $_.Exception; exit 1 }))
         | 
| 169 | 
            -
                            end
         | 
| 135 | 
            +
                          if Powershell.powershell_file?(remote_task_path) && stdin.nil?
         | 
| 136 | 
            +
                            conn.execute(Powershell.run_ps_task(arguments, remote_task_path, input_method))
         | 
| 170 137 | 
             
                          else
         | 
| 171 | 
            -
                            path, args = *process_from_extension(remote_task_path)
         | 
| 138 | 
            +
                            path, args = *Powershell.process_from_extension(remote_task_path)
         | 
| 172 139 | 
             
                            conn.execute_process(path, args, stdin)
         | 
| 173 140 | 
             
                          end
         | 
| 174 141 |  | 
| @@ -179,46 +146,6 @@ try { & "#{remote_task_path}" @taskArgs } catch { Write-Error $_.Exception; exit | |
| 179 146 | 
             
                    end
         | 
| 180 147 | 
             
                  end
         | 
| 181 148 |  | 
| 182 | 
            -
                  def powershell_file?(path)
         | 
| 183 | 
            -
                    Pathname(path).extname.casecmp('.ps1').zero?
         | 
| 184 | 
            -
                  end
         | 
| 185 | 
            -
             | 
| 186 | 
            -
                  def process_from_extension(path)
         | 
| 187 | 
            -
                    case Pathname(path).extname.downcase
         | 
| 188 | 
            -
                    when '.rb'
         | 
| 189 | 
            -
                      [
         | 
| 190 | 
            -
                        'ruby.exe',
         | 
| 191 | 
            -
                        ['-S', "\"#{path}\""]
         | 
| 192 | 
            -
                      ]
         | 
| 193 | 
            -
                    when '.ps1'
         | 
| 194 | 
            -
                      [
         | 
| 195 | 
            -
                        'powershell.exe',
         | 
| 196 | 
            -
                        [*PS_ARGS, '-File', "\"#{path}\""]
         | 
| 197 | 
            -
                      ]
         | 
| 198 | 
            -
                    when '.pp'
         | 
| 199 | 
            -
                      [
         | 
| 200 | 
            -
                        'puppet.bat',
         | 
| 201 | 
            -
                        ['apply', "\"#{path}\""]
         | 
| 202 | 
            -
                      ]
         | 
| 203 | 
            -
                    else
         | 
| 204 | 
            -
                      # Run the script via cmd, letting Windows extension handling determine how
         | 
| 205 | 
            -
                      [
         | 
| 206 | 
            -
                        'cmd.exe',
         | 
| 207 | 
            -
                        ['/c', "\"#{path}\""]
         | 
| 208 | 
            -
                      ]
         | 
| 209 | 
            -
                    end
         | 
| 210 | 
            -
                  end
         | 
| 211 | 
            -
             | 
| 212 | 
            -
                  def escape_arguments(arguments)
         | 
| 213 | 
            -
                    arguments.map do |arg|
         | 
| 214 | 
            -
                      if arg =~ / /
         | 
| 215 | 
            -
                        "\"#{arg}\""
         | 
| 216 | 
            -
                      else
         | 
| 217 | 
            -
                        arg
         | 
| 218 | 
            -
                      end
         | 
| 219 | 
            -
                    end
         | 
| 220 | 
            -
                  end
         | 
| 221 | 
            -
             | 
| 222 149 | 
             
                  def connected?(target)
         | 
| 223 150 | 
             
                    with_connection(target) { true }
         | 
| 224 151 | 
             
                  rescue Bolt::Node::ConnectError
         |