ruby-pwsh 0.2.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,120 @@
1
+ function new-pscredential {
2
+ [CmdletBinding()]
3
+ param (
4
+ [parameter(Mandatory = $true,
5
+ ValueFromPipelineByPropertyName = $true)]
6
+ [string]
7
+ $user,
8
+
9
+ [parameter(Mandatory = $true,
10
+ ValueFromPipelineByPropertyName = $true)]
11
+ [string]
12
+ $password
13
+ )
14
+
15
+ $secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
16
+ $credentials = New-Object System.Management.Automation.PSCredential ($user, $secpasswd)
17
+ return $credentials
18
+ }
19
+
20
+ Function ConvertTo-CanonicalResult {
21
+ [CmdletBinding()]
22
+ param(
23
+ [Parameter(Mandatory, Position = 1)]
24
+ [psobject]
25
+ $Result,
26
+
27
+ [Parameter(DontShow)]
28
+ [string]
29
+ $PropertyPath,
30
+
31
+ [Parameter(DontShow)]
32
+ [int]
33
+ $RecursionLevel = 0
34
+ )
35
+
36
+ $MaxDepth = 5
37
+ $CimInstancePropertyFilter = { $_.Definition -match 'CimInstance' -and $_.Name -ne 'PSDscRunAsCredential' }
38
+
39
+ # Get the properties which are/aren't Cim instances
40
+ $ResultObject = @{ }
41
+ $ResultPropertyList = $Result | Get-Member -MemberType Property | Where-Object { $_.Name -ne 'PSComputerName' }
42
+ $CimInstanceProperties = $ResultPropertyList | Where-Object -FilterScript $CimInstancePropertyFilter
43
+
44
+ foreach ($Property in $ResultPropertyList) {
45
+ $PropertyName = $Property.Name
46
+ if ($Property -notin $CimInstanceProperties) {
47
+ $Value = $Result.$PropertyName
48
+ if ($PropertyName -eq 'Ensure' -and [string]::IsNullOrEmpty($Result.$PropertyName)) {
49
+ # Just set 'Present' since it was found /shrug
50
+ # If the value IS listed as absent, don't update it unless you want flapping
51
+ $Value = 'Present'
52
+ }
53
+ else {
54
+ if ($Value -is [string] -or $value -is [string[]]) {
55
+ $Value = $Value
56
+ }
57
+
58
+ if ($Value.Count -eq 1 -and $Property.Definition -match '\\[\\]') {
59
+ $Value = @($Value)
60
+ }
61
+ }
62
+ }
63
+ elseif ($null -eq $Result.$PropertyName) {
64
+ if ($Property -match 'InstanceArray') {
65
+ $Value = @()
66
+ }
67
+ else {
68
+ $Value = $null
69
+ }
70
+ }
71
+ else {
72
+ # Looks like a nested CIM instance, recurse if we're not too deep in already.
73
+ $RecursionLevel++
74
+
75
+ if ($PropertyPath -eq [string]::Empty) {
76
+ $PropertyPath = $PropertyName
77
+ }
78
+ else {
79
+ $PropertyPath = "$PropertyPath.$PropertyName"
80
+ }
81
+
82
+ if ($RecursionLevel -gt $MaxDepth) {
83
+ # Give up recursing more than this
84
+ return $Result.ToString()
85
+ }
86
+
87
+ $Value = foreach ($item in $Result.$PropertyName) {
88
+ ConvertTo-CanonicalResult -Result $item -PropertyPath $PropertyPath -RecursionLevel ($RecursionLevel + 1) -WarningAction Continue
89
+ }
90
+
91
+ # The cim instance type is the last component of the type Name
92
+ # We need to return this for ruby to compare the result hashes
93
+ # We do NOT need it for the top-level properties as those are defined in the type
94
+ If ($RecursionLevel -gt 1 -and ![string]::IsNullOrEmpty($Value) ) {
95
+ # If there's multiple instances, you need to add the type to each one, but you
96
+ # need to specify only *one* name, otherwise things end up *very* broken.
97
+ if ($Value.GetType().Name -match '\[\]') {
98
+ $Value | ForEach-Object -Process {
99
+ $_.cim_instance_type = $Result.$PropertyName.CimClass.CimClassName[0]
100
+ }
101
+ } else {
102
+ $Value.cim_instance_type = $Result.$PropertyName.CimClass.CimClassName
103
+ # Ensure that, if it should be an array, it is
104
+ if ($Result.$PropertyName.GetType().Name -match '\[\]') {
105
+ $Value = @($Value)
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ if ($Property.Definition -match 'InstanceArray') {
112
+ if ($Value.Count -lt 2) { $Value = @($Value) }
113
+ }
114
+
115
+ $ResultObject.$PropertyName = $Value
116
+ }
117
+
118
+ # Output the final result
119
+ $ResultObject
120
+ }
@@ -0,0 +1,23 @@
1
+ Try {
2
+ $Result = Invoke-DscResource @InvokeParams
3
+ } catch {
4
+ $Response.errormessage = $_.Exception.Message
5
+ return ($Response | ConvertTo-Json -Compress)
6
+ }
7
+
8
+ # keep the switch for when Test passes back changed properties
9
+ Switch ($invokeParams.Method) {
10
+ 'Test' {
11
+ $Response.indesiredstate = $Result.InDesiredState
12
+ return ($Response | ConvertTo-Json -Compress)
13
+ }
14
+ 'Set' {
15
+ $Response.indesiredstate = $true
16
+ $Response.rebootrequired = $Result.RebootRequired
17
+ return ($Response | ConvertTo-Json -Compress)
18
+ }
19
+ 'Get' {
20
+ $CanonicalizedResult = ConvertTo-CanonicalResult -Result $Result
21
+ return ($CanonicalizedResult | ConvertTo-Json -Compress -Depth 10)
22
+ }
23
+ }
@@ -0,0 +1,8 @@
1
+ $script:ErrorActionPreference = 'Stop'
2
+ $script:WarningPreference = 'SilentlyContinue'
3
+
4
+ $response = @{
5
+ indesiredstate = $false
6
+ rebootrequired = $false
7
+ errormessage = ''
8
+ }
@@ -54,7 +54,7 @@ module Pwsh
54
54
  if manager.nil? || !manager.alive?
55
55
  # ignore any errors trying to tear down this unusable instance
56
56
  begin
57
- manager&.exit
57
+ manager.exit unless manager.nil? # rubocop:disable Style/SafeNavigation
58
58
  rescue
59
59
  nil
60
60
  end
@@ -117,6 +117,7 @@ module Pwsh
117
117
  # This named pipe path is Windows specific.
118
118
  pipe_path = "\\\\.\\pipe\\#{named_pipe_name}"
119
119
  else
120
+ require 'tmpdir'
120
121
  # .Net implements named pipes under Linux etc. as Unix Sockets in the filesystem
121
122
  # Paths that are rooted are not munged within C# Core.
122
123
  # https://github.com/dotnet/corefx/blob/94e9d02ad70b2224d012ac4a66eaa1f913ae4f29/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs#L49-L60
@@ -470,7 +471,7 @@ Invoke-PowerShellUserCode @params
470
471
  # @return [String] The UTF-8 encoded string containing the payload
471
472
  def self.read_length_prefixed_string!(bytes)
472
473
  # 32 bit integer in Little Endian format
473
- length = bytes.slice!(0, 4).unpack('V').first
474
+ length = bytes.slice!(0, 4).unpack1('V')
474
475
  return nil if length.zero?
475
476
 
476
477
  bytes.slice!(0, length).force_encoding(Encoding::UTF_8)
@@ -585,7 +586,7 @@ Invoke-PowerShellUserCode @params
585
586
 
586
587
  pipe_reader = Thread.new(@pipe) do |pipe|
587
588
  # Read a Little Endian 32-bit integer for length of response
588
- expected_response_length = pipe.sysread(4).unpack('V').first
589
+ expected_response_length = pipe.sysread(4).unpack1('V')
589
590
 
590
591
  next nil if expected_response_length.zero?
591
592
 
@@ -12,7 +12,7 @@ module Pwsh
12
12
  def on_windows?
13
13
  # Ruby only sets File::ALT_SEPARATOR on Windows and the Ruby standard
14
14
  # library uses that to test what platform it's on.
15
- !!File::ALT_SEPARATOR # rubocop:disable Style/DoubleNegation
15
+ !!File::ALT_SEPARATOR
16
16
  end
17
17
 
18
18
  # Verify paths specified are valid directories which exist.
@@ -31,50 +31,51 @@ module Pwsh
31
31
  invalid_paths
32
32
  end
33
33
 
34
- # Return a string converted to snake_case
34
+ # Return a string or symbol converted to snake_case
35
35
  #
36
36
  # @return [String] snake_cased string
37
- def snake_case(string)
37
+ def snake_case(object)
38
38
  # Implementation copied from: https://github.com/rubyworks/facets/blob/master/lib/core/facets/string/snakecase.rb
39
39
  # gsub(/::/, '/').
40
- string.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
41
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
42
- .tr('-', '_')
43
- .gsub(/\s/, '_')
44
- .gsub(/__+/, '_')
45
- .downcase
40
+ should_symbolize = object.is_a?(Symbol)
41
+ raise "snake_case method only handles strings and symbols, passed a #{object.class}: #{object}" unless should_symbolize || object.is_a?(String)
42
+
43
+ text = object.to_s
44
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
45
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
46
+ .tr('-', '_')
47
+ .gsub(/\s/, '_')
48
+ .gsub(/__+/, '_')
49
+ .downcase
50
+ should_symbolize ? text.to_sym : text
46
51
  end
47
52
 
48
53
  # Iterate through a hashes keys, snake_casing them
49
54
  #
50
55
  # @return [Hash] Hash with all keys snake_cased
51
- def snake_case_hash_keys(hash)
52
- modified_hash = {}
53
- hash.each do |key, value|
54
- value = snake_case_hash_keys(value) if value.is_a?(Hash)
55
- modified_hash[snake_case(key.to_s).to_sym] = value
56
- end
57
- modified_hash
56
+ def snake_case_hash_keys(object)
57
+ snake_case_proc = proc { |key| snake_case(key) }
58
+ apply_key_mutator(object, snake_case_proc)
58
59
  end
59
60
 
60
- # Return a string converted to PascalCase
61
+ # Return a string or symbol converted to PascalCase
61
62
  #
62
63
  # @return [String] PascalCased string
63
- def pascal_case(string)
64
+ def pascal_case(object)
65
+ should_symbolize = object.is_a?(Symbol)
66
+ raise "snake_case method only handles strings and symbols, passed a #{object.class}: #{object}" unless should_symbolize || object.is_a?(String)
67
+
64
68
  # Break word boundaries to snake case first
65
- snake_case(string).split('_').collect(&:capitalize).join
69
+ text = snake_case(object.to_s).split('_').collect(&:capitalize).join
70
+ should_symbolize ? text.to_sym : text
66
71
  end
67
72
 
68
73
  # Iterate through a hashes keys, PascalCasing them
69
74
  #
70
75
  # @return [Hash] Hash with all keys PascalCased
71
- def pascal_case_hash_keys(hash)
72
- modified_hash = {}
73
- hash.each do |key, value|
74
- value = pascal_case_hash_keys(value) if value.is_a?(Hash)
75
- modified_hash[pascal_case(key.to_s).to_sym] = value
76
- end
77
- modified_hash
76
+ def pascal_case_hash_keys(object)
77
+ pascal_case_proc = proc { |key| pascal_case(key) }
78
+ apply_key_mutator(object, pascal_case_proc)
78
79
  end
79
80
 
80
81
  # Ensure that quotes inside a passed string will continue to be passed
@@ -84,6 +85,27 @@ module Pwsh
84
85
  text.gsub("'", "''")
85
86
  end
86
87
 
88
+ # Ensure that all keys in a hash are symbols, not strings.
89
+ #
90
+ # @return [Hash] a hash whose keys have been converted to symbols.
91
+ def symbolize_hash_keys(object)
92
+ symbolize_proc = proc(&:to_sym)
93
+ apply_key_mutator(object, symbolize_proc)
94
+ end
95
+
96
+ def apply_key_mutator(object, proc)
97
+ return object.map { |item| apply_key_mutator(item, proc) } if object.is_a?(Array)
98
+ return object unless object.is_a?(Hash)
99
+
100
+ modified_hash = {}
101
+ object.each do |key, value|
102
+ modified_hash[proc.call(key)] = apply_key_mutator(value, proc)
103
+ end
104
+ modified_hash
105
+ end
106
+
107
+ private_class_method :apply_key_mutator
108
+
87
109
  # Convert a ruby value into a string to be passed along to PowerShell for interpolation in a command
88
110
  # Handles:
89
111
  # - Strings
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Pwsh
4
4
  # The version of the ruby-pwsh gem
5
- VERSION = '0.2.0'
5
+ VERSION = '0.5.1'
6
6
  end
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "puppetlabs-pwshlib",
3
- "version": "0.2.0",
3
+ "version": "0.5.1",
4
4
  "author": "puppetlabs",
5
5
  "summary": "Provide library code for interoperating with PowerShell.",
6
6
  "license": "MIT",
7
7
  "source": "https://github.com/puppetlabs/ruby-pwsh",
8
- "project_page": "https://github.com/puppetlabs/ruby-pwsh/pwshlib.md",
8
+ "project_page": "https://github.com/puppetlabs/ruby-pwsh/blob/master/pwshlib.md",
9
9
  "issues_url": "https://github.com/puppetlabs/ruby-pwsh/issues",
10
10
  "dependencies": [
11
11
 
@@ -76,7 +76,7 @@
76
76
  "requirements": [
77
77
  {
78
78
  "name": "puppet",
79
- "version_requirement": ">= 4.10.0 < 7.0.0"
79
+ "version_requirement": ">= 5.5.0 < 7.0.0"
80
80
  }
81
81
  ],
82
82
  "pdk-version": "1.13.0",
data/pwshlib.md CHANGED
@@ -88,3 +88,5 @@ Puppet.debug(posh.execute('[String]$PSVersionTable.PSVersion'))
88
88
  ps_version = posh.execute('[String]$PSVersionTable.PSVersion')[:stdout].strip
89
89
  Puppet.debug("The PowerShell version of the currently running Manager is #{ps_version}")
90
90
  ```
91
+
92
+ For more information, please review the [online reference documentation for the gem](https://rubydoc.info/gems/ruby-pwsh).
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-pwsh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-26 00:00:00.000000000 Z
11
+ date: 2020-09-25 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: PowerShell code manager for ruby.
14
14
  email:
@@ -31,7 +31,13 @@ files:
31
31
  - LICENSE.txt
32
32
  - README.md
33
33
  - Rakefile
34
+ - appveyor.yml
34
35
  - design-comms.png
36
+ - lib/puppet/feature/pwshlib.rb
37
+ - lib/puppet/provider/dsc_base_provider/dsc_base_provider.rb
38
+ - lib/puppet/provider/dsc_base_provider/invoke_dsc_resource_functions.ps1
39
+ - lib/puppet/provider/dsc_base_provider/invoke_dsc_resource_postscript.ps1
40
+ - lib/puppet/provider/dsc_base_provider/invoke_dsc_resource_preamble.ps1
35
41
  - lib/pwsh.rb
36
42
  - lib/pwsh/util.rb
37
43
  - lib/pwsh/version.rb