ruby-pwsh 0.2.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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