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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.pmtignore +2 -0
- data/.rubocop.yml +12 -2
- data/.travis.yml +1 -0
- data/CHANGELOG.md +46 -2
- data/Gemfile +4 -9
- data/README.md +50 -0
- data/Rakefile +1 -3
- data/appveyor.yml +38 -0
- data/lib/puppet/feature/pwshlib.rb +5 -0
- data/lib/puppet/provider/dsc_base_provider/dsc_base_provider.rb +670 -0
- data/lib/puppet/provider/dsc_base_provider/invoke_dsc_resource_functions.ps1 +120 -0
- data/lib/puppet/provider/dsc_base_provider/invoke_dsc_resource_postscript.ps1 +23 -0
- data/lib/puppet/provider/dsc_base_provider/invoke_dsc_resource_preamble.ps1 +8 -0
- data/lib/pwsh.rb +4 -3
- data/lib/pwsh/util.rb +48 -26
- data/lib/pwsh/version.rb +1 -1
- data/metadata.json +3 -3
- data/pwshlib.md +2 -0
- metadata +8 -2
@@ -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
|
+
}
|
data/lib/pwsh.rb
CHANGED
@@ -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
|
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).
|
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).
|
589
|
+
expected_response_length = pipe.sysread(4).unpack1('V')
|
589
590
|
|
590
591
|
next nil if expected_response_length.zero?
|
591
592
|
|
data/lib/pwsh/util.rb
CHANGED
@@ -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
|
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(
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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(
|
52
|
-
|
53
|
-
|
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(
|
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(
|
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(
|
72
|
-
|
73
|
-
|
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
|
data/lib/pwsh/version.rb
CHANGED
data/metadata.json
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
{
|
2
2
|
"name": "puppetlabs-pwshlib",
|
3
|
-
"version": "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": ">=
|
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.
|
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:
|
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
|