ruby-pwsh 0.5.1 → 0.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +52 -0
- data/CONTRIBUTING.md +1 -1
- data/README.md +4 -4
- data/appveyor.yml +1 -1
- data/lib/puppet/provider/dsc_base_provider/dsc_base_provider.rb +166 -47
- data/lib/puppet/provider/dsc_base_provider/invoke_dsc_resource_functions.ps1 +4 -0
- data/lib/pwsh.rb +3 -3
- data/lib/pwsh/util.rb +7 -7
- data/lib/pwsh/version.rb +1 -1
- data/metadata.json +2 -2
- metadata +6 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8334c3715e69873cda019ad2010187d15ad34a2b593426b567d056a4a1fc364
|
4
|
+
data.tar.gz: e66f5fccc419fc4d315c7db3def945bb3108d57c756bf55e5205039d1963c6c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2522c738352df33e552c0fa26b48113b95b65ceece2cc03bbb48d731df5e1fc623ec8590e18b8152205fb07ff33fc1f1f1892ccbc6e399a729a61732385a48c7
|
7
|
+
data.tar.gz: 43c5e11f365df85d1b45978bc3ae8855668bd97168ac24ef9d3bffd77b9e4a709b0eb71872d19b63c0225f2c08061dfa346d8e2c7fd52133afd283c14db54b2c
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,58 @@
|
|
2
2
|
|
3
3
|
All notable changes to this project will be documented in this file.The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org).
|
4
4
|
|
5
|
+
## [0.7.0](https://github.com/puppetlabs/ruby-pwsh/tree/0.7.0) (2021-01-20)
|
6
|
+
|
7
|
+
[Full Changelog](https://github.com/puppetlabs/ruby-pwsh/compare/0.6.3...0.7.0)
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- \(GH-75\) Including module name in vendored module path [\#85](https://github.com/puppetlabs/ruby-pwsh/pull/85) ([pmcmaw](https://github.com/pmcmaw))
|
12
|
+
|
13
|
+
### Fixed
|
14
|
+
|
15
|
+
- Make root module path use puppetized module name [\#86](https://github.com/puppetlabs/ruby-pwsh/pull/86) ([michaeltlombardi](https://github.com/michaeltlombardi))
|
16
|
+
|
17
|
+
## [0.6.3](https://github.com/puppetlabs/ruby-pwsh/tree/0.6.3) (2020-12-16)
|
18
|
+
|
19
|
+
[Full Changelog](https://github.com/puppetlabs/ruby-pwsh/compare/0.6.2...0.6.3)
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
|
23
|
+
- \(MAINT\) Add handling for when dsc\_ensure is stripped [\#78](https://github.com/puppetlabs/ruby-pwsh/pull/78) ([michaeltlombardi](https://github.com/michaeltlombardi))
|
24
|
+
|
25
|
+
## [0.6.2](https://github.com/puppetlabs/ruby-pwsh/tree/0.6.2) (2020-12-09)
|
26
|
+
|
27
|
+
[Full Changelog](https://github.com/puppetlabs/ruby-pwsh/compare/0.6.1...0.6.2)
|
28
|
+
|
29
|
+
### Fixed
|
30
|
+
|
31
|
+
- \(MAINT\) Ensure parameters are canonicalized [\#75](https://github.com/puppetlabs/ruby-pwsh/pull/75) ([michaeltlombardi](https://github.com/michaeltlombardi))
|
32
|
+
|
33
|
+
## [0.6.1](https://github.com/puppetlabs/ruby-pwsh/tree/0.6.1) (2020-11-25)
|
34
|
+
|
35
|
+
[Full Changelog](https://github.com/puppetlabs/ruby-pwsh/compare/0.6.0...0.6.1)
|
36
|
+
|
37
|
+
### Fixed
|
38
|
+
|
39
|
+
- \(maint\) - Removal of inappropriate terminology [\#70](https://github.com/puppetlabs/ruby-pwsh/pull/70) ([pmcmaw](https://github.com/pmcmaw))
|
40
|
+
- \(Maint\) Fix ensurability in the dsc base provider [\#69](https://github.com/puppetlabs/ruby-pwsh/pull/69) ([michaeltlombardi](https://github.com/michaeltlombardi))
|
41
|
+
|
42
|
+
## [0.6.0](https://github.com/puppetlabs/ruby-pwsh/tree/0.6.0) (2020-11-24)
|
43
|
+
|
44
|
+
[Full Changelog](https://github.com/puppetlabs/ruby-pwsh/compare/0.5.1...0.6.0)
|
45
|
+
|
46
|
+
### Added
|
47
|
+
|
48
|
+
- \(GH-81\) Handle parameters in the dsc base provider [\#62](https://github.com/puppetlabs/ruby-pwsh/pull/62) ([michaeltlombardi](https://github.com/michaeltlombardi))
|
49
|
+
- \(GH-74\) Remove special handling for ensure in the dsc base provider [\#61](https://github.com/puppetlabs/ruby-pwsh/pull/61) ([michaeltlombardi](https://github.com/michaeltlombardi))
|
50
|
+
- \(GH-59\) Refactor away from Simple Provider [\#60](https://github.com/puppetlabs/ruby-pwsh/pull/60) ([michaeltlombardi](https://github.com/michaeltlombardi))
|
51
|
+
|
52
|
+
### Fixed
|
53
|
+
|
54
|
+
- \(GH-57\) Handle datetimes in dsc [\#58](https://github.com/puppetlabs/ruby-pwsh/pull/58) ([michaeltlombardi](https://github.com/michaeltlombardi))
|
55
|
+
- \(GH-55\) Handle intentionally empty arrays [\#56](https://github.com/puppetlabs/ruby-pwsh/pull/56) ([michaeltlombardi](https://github.com/michaeltlombardi))
|
56
|
+
|
5
57
|
## [0.5.1](https://github.com/puppetlabs/ruby-pwsh/tree/0.5.1) (2020-09-25)
|
6
58
|
|
7
59
|
[Full Changelog](https://github.com/puppetlabs/ruby-pwsh/compare/0.5.0...0.5.1)
|
data/CONTRIBUTING.md
CHANGED
@@ -79,7 +79,7 @@ process as easy as possible.
|
|
79
79
|
|
80
80
|
2. Sending your patches
|
81
81
|
|
82
|
-
To submit your changes via a GitHub pull request, we _highly_ recommend that you have them on a topic branch, instead of directly on "
|
82
|
+
To submit your changes via a GitHub pull request, we _highly_ recommend that you have them on a topic branch, instead of directly on "main".
|
83
83
|
It makes things much easier to keep track of, especially if you decide to work on another thing before your first change is merged in.
|
84
84
|
|
85
85
|
GitHub has some pretty good [general documentation](http://help.github.com/) on using their site.
|
data/README.md
CHANGED
@@ -74,9 +74,9 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
74
74
|
|
75
75
|
Steps to release an update to the gem and module include:
|
76
76
|
|
77
|
-
1. Ensure that the release branch is up to date with the
|
77
|
+
1. Ensure that the release branch is up to date with the main:
|
78
78
|
```bash
|
79
|
-
git push upstream upstream/
|
79
|
+
git push upstream upstream/main:release --force
|
80
80
|
```
|
81
81
|
1. Checkout a new working branch for the release prep (where xyz is the appropriate version, sans periods):
|
82
82
|
```bash
|
@@ -96,7 +96,7 @@ Steps to release an update to the gem and module include:
|
|
96
96
|
```
|
97
97
|
1. Push your changes and submit a pull request for review _against the **release** branch_:
|
98
98
|
```bash
|
99
|
-
git push -u origin maint/release
|
99
|
+
git push -u origin maint/release/prep-xyz
|
100
100
|
```
|
101
101
|
1. Ensure tests pass and the code is merged to `release`.
|
102
102
|
1. Grab the commit hash from the merge commit on release, use that as the tag for the version (replacing `x.y.z` with the appropriate version and `commithash` with the relevant one), then push the tags to upstream:
|
@@ -114,6 +114,6 @@ Steps to release an update to the gem and module include:
|
|
114
114
|
bundle exec rake build_module
|
115
115
|
```
|
116
116
|
1. Publish the updated module version (found in the `pkg` folder) to [the Forge](https://forge.puppet.com/puppetlabs/pwshlib).
|
117
|
-
1. Submit the [mergeback PR from the release branch to
|
117
|
+
1. Submit the [mergeback PR from the release branch to main](https://github.com/puppetlabs/ruby-pwsh/compare/main...release).
|
118
118
|
|
119
119
|
## Known Issues
|
data/appveyor.yml
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'puppet/resource_api/simple_provider'
|
4
3
|
require 'securerandom'
|
5
4
|
require 'ruby-pwsh'
|
6
5
|
require 'pathname'
|
7
6
|
require 'json'
|
8
7
|
|
9
|
-
class Puppet::Provider::DscBaseProvider
|
8
|
+
class Puppet::Provider::DscBaseProvider
|
10
9
|
# Initializes the provider, preparing the class variables which cache:
|
11
10
|
# - the canonicalized resources across calls
|
12
11
|
# - query results
|
@@ -15,6 +14,7 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
15
14
|
@@cached_canonicalized_resource = []
|
16
15
|
@@cached_query_results = []
|
17
16
|
@@logon_failures = []
|
17
|
+
super
|
18
18
|
end
|
19
19
|
|
20
20
|
# Look through a cache to retrieve the hashes specified, if they have been cached.
|
@@ -50,6 +50,8 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
50
50
|
canonicalized = r.dup
|
51
51
|
@@cached_canonicalized_resource << r.dup
|
52
52
|
else
|
53
|
+
parameters = r.select { |name, _properties| parameter_attributes(context).include?(name) }
|
54
|
+
canonicalized.merge!(parameters)
|
53
55
|
canonicalized[:name] = r[:name]
|
54
56
|
if r[:dsc_psdscrunascredential].nil?
|
55
57
|
canonicalized.delete(:dsc_psdscrunascredential)
|
@@ -109,6 +111,78 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
109
111
|
end
|
110
112
|
end
|
111
113
|
|
114
|
+
# Determines whether a resource is ensurable and which message to write (create, update, or delete),
|
115
|
+
# then passes the appropriate values along to the various sub-methods which themselves call the Set
|
116
|
+
# method of Invoke-DscResource. Implementation borrowed directly from the Resource API Simple Provider
|
117
|
+
#
|
118
|
+
# @param context [Object] the Puppet runtime context to operate in and send feedback to
|
119
|
+
# @param changes [Hash] the hash of whose key is the name_hash and value is the is and should hashes
|
120
|
+
def set(context, changes)
|
121
|
+
changes.each do |name, change|
|
122
|
+
is = change.key?(:is) ? change[:is] : (get(context, [name]) || []).find { |r| r[:name] == name }
|
123
|
+
context.type.check_schema(is) unless change.key?(:is)
|
124
|
+
|
125
|
+
should = change[:should]
|
126
|
+
|
127
|
+
name_hash = if context.type.namevars.length > 1
|
128
|
+
# pass a name_hash containing the values of all namevars
|
129
|
+
name_hash = {}
|
130
|
+
context.type.namevars.each do |namevar|
|
131
|
+
name_hash[namevar] = change[:should][namevar]
|
132
|
+
end
|
133
|
+
name_hash
|
134
|
+
else
|
135
|
+
name
|
136
|
+
end
|
137
|
+
|
138
|
+
# for compatibility sake, we use dsc_ensure instead of ensure, so context.type.ensurable? does not work
|
139
|
+
if context.type.attributes.key?(:dsc_ensure)
|
140
|
+
is = create_absent(:name, name) if is.nil?
|
141
|
+
should = create_absent(:name, name) if should.nil?
|
142
|
+
|
143
|
+
# HACK: If the DSC Resource is ensurable but doesn't report a default value
|
144
|
+
# for ensure, we assume it to be `Present` - this is the most common pattern.
|
145
|
+
should_ensure = should[:dsc_ensure].nil? ? 'Present' : should[:dsc_ensure].to_s
|
146
|
+
# HACK: Sometimes dsc_ensure is removed???? If it's gone, pretend it's absent??
|
147
|
+
is_ensure = is[:dsc_ensure].nil? ? 'Absent' : is[:dsc_ensure].to_s
|
148
|
+
|
149
|
+
if is_ensure == 'Absent' && should_ensure == 'Present'
|
150
|
+
context.creating(name) do
|
151
|
+
create(context, name_hash, should)
|
152
|
+
end
|
153
|
+
elsif is_ensure == 'Present' && should_ensure == 'Present'
|
154
|
+
context.updating(name) do
|
155
|
+
update(context, name_hash, should)
|
156
|
+
end
|
157
|
+
elsif is_ensure == 'Present' && should_ensure == 'Absent'
|
158
|
+
context.deleting(name) do
|
159
|
+
delete(context, name_hash)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
else
|
163
|
+
context.updating(name) do
|
164
|
+
update(context, name_hash, should)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Creates a hash with the name / name_hash and sets dsc_ensure to absent for comparison
|
171
|
+
# purposes; this handles cases where the resource isn't found on the node.
|
172
|
+
#
|
173
|
+
# @param namevar [Object] the name of the variable being used for the resource name
|
174
|
+
# @param title [Hash] the hash of namevar properties and their values
|
175
|
+
# @return [Hash] returns a hash representing the absent state of the resource
|
176
|
+
def create_absent(namevar, title)
|
177
|
+
result = if title.is_a? Hash
|
178
|
+
title.dup
|
179
|
+
else
|
180
|
+
{ namevar => title }
|
181
|
+
end
|
182
|
+
result[:dsc_ensure] = 'Absent'
|
183
|
+
result
|
184
|
+
end
|
185
|
+
|
112
186
|
# Attempts to set an instance of the DSC resource, invoking the `Set` method and thinly wrapping
|
113
187
|
# the `invoke_set_method` method; whether this method, `update`, or `delete` is called is entirely
|
114
188
|
# up to the Resource API based on the results
|
@@ -158,9 +232,7 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
158
232
|
context.debug("retrieving #{name_hash.inspect}")
|
159
233
|
|
160
234
|
# Do not bother running if the logon credentials won't work
|
161
|
-
|
162
|
-
return name_hash if logon_failed_already?(name_hash[:dsc_psdscrunascredential])
|
163
|
-
end
|
235
|
+
return name_hash if !name_hash[:dsc_psdscrunascredential].nil? && logon_failed_already?(name_hash[:dsc_psdscrunascredential])
|
164
236
|
|
165
237
|
query_props = name_hash.select { |k, v| mandatory_get_attributes(context).include?(k) || (k == :dsc_psdscrunascredential && !v.nil?) }
|
166
238
|
resource = should_to_resource(query_props, context, 'get')
|
@@ -190,28 +262,36 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
190
262
|
# DSC gives back information we don't care about; filter down to only
|
191
263
|
# those properties exposed in the type definition.
|
192
264
|
valid_attributes = context.type.attributes.keys.collect(&:to_s)
|
265
|
+
parameters = context.type.attributes.select { |_name, properties| [properties[:behaviour]].collect.include?(:parameter) }.keys.collect(&:to_s)
|
193
266
|
data.select! { |key, _value| valid_attributes.include?("dsc_#{key.downcase}") }
|
267
|
+
data.reject! { |key, _value| parameters.include?("dsc_#{key.downcase}") }
|
194
268
|
# Canonicalize the results to match the type definition representation;
|
195
269
|
# failure to do so will prevent the resource_api from comparing the result
|
196
270
|
# to the should hash retrieved from the resource definition in the manifest.
|
197
|
-
data.keys.each do |key|
|
271
|
+
data.keys.each do |key| # rubocop:disable Style/HashEachMethods
|
198
272
|
type_key = "dsc_#{key.downcase}".to_sym
|
199
273
|
data[type_key] = data.delete(key)
|
200
274
|
camelcase_hash_keys!(data[type_key]) if data[type_key].is_a?(Enumerable)
|
275
|
+
# Convert DateTime back to appropriate type
|
276
|
+
data[type_key] = Puppet::Pops::Time::Timestamp.parse(data[type_key]) if context.type.attributes[type_key][:mof_type] =~ /DateTime/i
|
277
|
+
# PowerShell does not distinguish between a return of empty array/string
|
278
|
+
# and null but Puppet does; revert to those values if specified.
|
279
|
+
if data[type_key].nil? && query_props.keys.include?(type_key) && query_props[type_key].is_a?(Array)
|
280
|
+
data[type_key] = query_props[type_key].empty? ? query_props[type_key] : []
|
281
|
+
end
|
201
282
|
end
|
202
|
-
# If a resource is found, it's present, so refill
|
283
|
+
# If a resource is found, it's present, so refill this Puppet-only key
|
203
284
|
data.merge!({ name: name_hash[:name] })
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
#
|
209
|
-
|
210
|
-
if
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
end
|
285
|
+
|
286
|
+
# Have to check for this to avoid a weird canonicalization warning
|
287
|
+
# The Resource API calls canonicalize against the current state which
|
288
|
+
# will lead to dsc_ensure being set to absent in the name_hash even if
|
289
|
+
# it was set to present in the manifest
|
290
|
+
name_hash_has_nil_keys = name_hash.select { |_k, v| v.nil? }.count.positive?
|
291
|
+
# We want to throw away all of the empty keys if and only if the manifest
|
292
|
+
# declaration is for an absent resource and the resource is actually absent
|
293
|
+
data.reject! { |_k, v| v.nil? } if data[:dsc_ensure] == 'Absent' && name_hash[:dsc_ensure] == 'Absent' && !name_hash_has_nil_keys
|
294
|
+
|
215
295
|
# Cache the query to prevent a second lookup
|
216
296
|
@@cached_query_results << data.dup if fetch_cached_hashes(@@cached_query_results, [data]).empty?
|
217
297
|
context.debug("Returned to Puppet as #{data}")
|
@@ -229,9 +309,7 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
229
309
|
context.debug("Invoking Set Method for '#{name}' with #{should.inspect}")
|
230
310
|
|
231
311
|
# Do not bother running if the logon credentials won't work
|
232
|
-
|
233
|
-
return nil if logon_failed_already?(should[:dsc_psdscrunascredential])
|
234
|
-
end
|
312
|
+
return nil if !should[:dsc_psdscrunascredential].nil? && logon_failed_already?(should[:dsc_psdscrunascredential])
|
235
313
|
|
236
314
|
apply_props = should.select { |k, _v| k.to_s =~ /^dsc_/ }
|
237
315
|
resource = should_to_resource(apply_props, context, 'set')
|
@@ -265,7 +343,6 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
265
343
|
resource[k] = context.type.definition[k]
|
266
344
|
end
|
267
345
|
should.each do |k, v|
|
268
|
-
next if k == :ensure
|
269
346
|
# PSDscRunAsCredential is considered a namevar and will always be passed, even if nil
|
270
347
|
# To prevent flapping during runs, remove it from the resource definition unless specified
|
271
348
|
next if k == :dsc_psdscrunascredential && v.nil?
|
@@ -281,17 +358,47 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
281
358
|
# Because Puppet adds all of the modules to the LOAD_PATH we can be sure that the appropriate module lives here during an apply;
|
282
359
|
# PROBLEM: This currently uses the downcased name, we need to capture the module name in the metadata I think.
|
283
360
|
# During a Puppet agent run, the code lives in the cache so we can use the file expansion to discover the correct folder.
|
284
|
-
root_module_path = $LOAD_PATH.select { |path| path.match?(%r{#{resource[:dscmeta_module_name]
|
361
|
+
root_module_path = $LOAD_PATH.select { |path| path.match?(%r{#{puppetize_name(resource[:dscmeta_module_name])}/lib}) }.first
|
285
362
|
resource[:vendored_modules_path] = if root_module_path.nil?
|
286
|
-
File.expand_path(Pathname.new(__FILE__).dirname + '../../../' + 'puppet_x/dsc_resources')
|
363
|
+
File.expand_path(Pathname.new(__FILE__).dirname + '../../../' + 'puppet_x/dsc_resources') # rubocop:disable Style/StringConcatenation
|
287
364
|
else
|
288
|
-
File.expand_path(root_module_path
|
365
|
+
File.expand_path("#{root_module_path}/puppet_x/dsc_resources")
|
289
366
|
end
|
367
|
+
|
368
|
+
# This handles setting the vendored_modules_path to include the puppet module name
|
369
|
+
# We now add the puppet module name into the path to allow multiple modules to with shared dsc_resources to be installed side by side
|
370
|
+
# The old vendored_modules_path: puppet_x/dsc_resources
|
371
|
+
# The new vendored_modules_path: puppet_x/<module_name>/dsc_resources
|
372
|
+
unless File.exist? resource[:vendored_modules_path]
|
373
|
+
resource[:vendored_modules_path] = if root_module_path.nil?
|
374
|
+
File.expand_path(Pathname.new(__FILE__).dirname + '../../../' + "puppet_x/#{puppetize_name(resource[:dscmeta_module_name])}/dsc_resources") # rubocop:disable Style/StringConcatenation
|
375
|
+
else
|
376
|
+
File.expand_path("#{root_module_path}/puppet_x/#{puppetize_name(resource[:dscmeta_module_name])}/dsc_resources")
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
# A warning is thrown if the something went wrong and the file was not created
|
381
|
+
raise "Unable to find expected vendored DSC Resource #{resource[:vendored_modules_path]}" unless File.exist? resource[:vendored_modules_path]
|
382
|
+
|
290
383
|
resource[:attributes] = nil
|
384
|
+
|
291
385
|
context.debug("should_to_resource: #{resource.inspect}")
|
292
386
|
resource
|
293
387
|
end
|
294
388
|
|
389
|
+
# Return a String containing a puppetized name. A puppetized name is a string that only
|
390
|
+
# includes lowercase letters, digits, underscores and cannot start with a digit.
|
391
|
+
#
|
392
|
+
# @return [String] with a puppeized module name
|
393
|
+
def puppetize_name(name)
|
394
|
+
# Puppet module names must be lower case
|
395
|
+
name = name.downcase
|
396
|
+
# Puppet module names must only include lowercase letters, digits and underscores
|
397
|
+
name = name.gsub(/[^a-z0-9_]/, '_')
|
398
|
+
# Puppet module names must not start with a number so if it does, append the letter 'a' to the name. Eg: '7zip' becomes 'a7zip'
|
399
|
+
name = name.match?(/^\d/) ? "a#{name}" : name # rubocop:disable Lint/UselessAssignment
|
400
|
+
end
|
401
|
+
|
295
402
|
# Return a UUID with the dashes turned into underscores to enable the specifying of guaranteed-unique
|
296
403
|
# variables in the PowerShell script.
|
297
404
|
#
|
@@ -331,7 +438,7 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
331
438
|
# @return [Enumerable] returns the input object with hash keys recursively camelCased
|
332
439
|
def camelcase_hash_keys!(enumerable)
|
333
440
|
if enumerable.is_a?(Hash)
|
334
|
-
enumerable.keys.each do |key|
|
441
|
+
enumerable.keys.each do |key| # rubocop:disable Style/HashEachMethods
|
335
442
|
name = key.dup
|
336
443
|
name[0] = name[0].downcase
|
337
444
|
enumerable[name] = enumerable.delete(key)
|
@@ -363,14 +470,6 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
363
470
|
end
|
364
471
|
end
|
365
472
|
|
366
|
-
# Checks to see whether the DSC resource being managed is defined as ensurable
|
367
|
-
#
|
368
|
-
# @param context [Object] the Puppet runtime context to operate in and send feedback to
|
369
|
-
# @return [Bool] returns true if the DSC Resource is ensurable, otherwise false.
|
370
|
-
def ensurable?(context)
|
371
|
-
context.type.attributes.keys.include?(:ensure)
|
372
|
-
end
|
373
|
-
|
374
473
|
# Parses the DSC resource type definition to retrieve the names of any attributes which are specified as mandatory for get operations
|
375
474
|
#
|
376
475
|
# @param context [Object] the Puppet runtime context to operate in and send feedback to
|
@@ -395,6 +494,14 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
395
494
|
context.type.attributes.select { |_attribute, properties| properties[:behaviour] == :namevar }.keys
|
396
495
|
end
|
397
496
|
|
497
|
+
# Parses the DSC resource type definition to retrieve the names of any attributes which are specified as parameters
|
498
|
+
#
|
499
|
+
# @param context [Object] the Puppet runtime context to operate in and send feedback to
|
500
|
+
# @return [Array] returns an array of attribute names as symbols which are parameters
|
501
|
+
def parameter_attributes(context)
|
502
|
+
context.type.attributes.select { |_name, properties| properties[:behaviour] == :parameter }.keys
|
503
|
+
end
|
504
|
+
|
398
505
|
# Look through a fully formatted string, replacing all instances where a value matches the formatted properties
|
399
506
|
# of an instantiated variable with references to the variable instead. This allows us to pass complex and nested
|
400
507
|
# CIM instances to the Invoke-DscResource parameter hash without constructing them *in* the hash.
|
@@ -441,8 +548,7 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
441
548
|
# @param credential_hash [Hash] the Properties which define the PSCredential Object
|
442
549
|
# @return [String] A line of PowerShell which defines the PSCredential object and stores it to a variable
|
443
550
|
def format_pscredential(variable_name, credential_hash)
|
444
|
-
|
445
|
-
definition
|
551
|
+
"$#{variable_name} = New-PSCredential -User #{credential_hash['user']} -Password '#{credential_hash['password']}' # PuppetSensitive"
|
446
552
|
end
|
447
553
|
|
448
554
|
# Parses a resource definition (as from `should_to_resource`) for any properties which are CIM instances
|
@@ -522,8 +628,7 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
522
628
|
# EVEN WORSE HACK - this one we can't even be sure it's a cim instance...
|
523
629
|
# but I don't _think_ anything but nested cim instances show up as hashes inside an array
|
524
630
|
definition = definition.gsub('@(@{', '[CimInstance[]]@(@{')
|
525
|
-
|
526
|
-
definition
|
631
|
+
interpolate_variables(definition)
|
527
632
|
end
|
528
633
|
|
529
634
|
# Munge a resource definition (as from `should_to_resource`) into valid PowerShell which represents
|
@@ -548,18 +653,32 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
548
653
|
resource[:parameters].each do |property_name, property_hash|
|
549
654
|
# strip dsc_ from the beginning of the property name declaration
|
550
655
|
name = property_name.to_s.gsub(/^dsc_/, '').to_sym
|
551
|
-
params[:Property][name] =
|
656
|
+
params[:Property][name] = case property_hash[:mof_type]
|
657
|
+
when 'PSCredential'
|
552
658
|
# format can't unwrap Sensitive strings nested in arbitrary hashes/etc, so make
|
553
659
|
# the Credential hash interpolable as it will be replaced by a variable reference.
|
554
660
|
{
|
555
661
|
'user' => property_hash[:value]['user'],
|
556
662
|
'password' => escape_quotes(property_hash[:value]['password'].unwrap)
|
557
663
|
}
|
664
|
+
when 'DateTime'
|
665
|
+
# These have to be handled specifically because they rely on the *Puppet* DateTime,
|
666
|
+
# not a generic ruby data type (and so can't go in the shared util in ruby-pwsh)
|
667
|
+
"[DateTime]#{property_hash[:value].format('%FT%T%z')}"
|
558
668
|
else
|
559
669
|
property_hash[:value]
|
560
670
|
end
|
561
671
|
end
|
562
672
|
params_block = interpolate_variables("$InvokeParams = #{format(params)}")
|
673
|
+
# Move the Apostrophe for DateTime declarations
|
674
|
+
params_block = params_block.gsub("'[DateTime]", "[DateTime]'")
|
675
|
+
# HACK: Handle intentionally empty arrays - need to strongly type them because
|
676
|
+
# CIM instances do not do a consistent job of casting an empty array properly.
|
677
|
+
empty_array_parameters = resource[:parameters].select { |_k, v| v[:value].empty? }
|
678
|
+
empty_array_parameters.each do |name, properties|
|
679
|
+
param_block_name = name.to_s.gsub(/^dsc_/, '')
|
680
|
+
params_block = params_block.gsub("#{param_block_name} = @()", "#{param_block_name} = [#{properties[:mof_type]}]@()")
|
681
|
+
end
|
563
682
|
# HACK: make CIM instances work:
|
564
683
|
resource[:parameters].select { |_key, hash| hash[:mof_is_embedded] && hash[:mof_type] =~ /\[\]/ }.each do |_property_name, property_hash|
|
565
684
|
formatted_property_hash = interpolate_variables(format(property_hash[:value]))
|
@@ -577,11 +696,11 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
577
696
|
def ps_script_content(resource)
|
578
697
|
template_path = File.expand_path('../', __FILE__)
|
579
698
|
# Defines the helper functions
|
580
|
-
functions = File.new(template_path
|
699
|
+
functions = File.new("#{template_path}/invoke_dsc_resource_functions.ps1").read
|
581
700
|
# Defines the response hash and the runtime settings
|
582
|
-
preamble = File.new(template_path
|
701
|
+
preamble = File.new("#{template_path}/invoke_dsc_resource_preamble.ps1").read
|
583
702
|
# The postscript defines the invocation error and result handling; expects `$InvokeParams` to be defined
|
584
|
-
postscript = File.new(template_path
|
703
|
+
postscript = File.new("#{template_path}/invoke_dsc_resource_postscript.ps1").read
|
585
704
|
# The blocks define the variables to define for the postscript.
|
586
705
|
credential_block = prepare_credentials(resource)
|
587
706
|
cim_instances_block = prepare_cim_instances(resource)
|
@@ -589,8 +708,7 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
589
708
|
# clean them out of the temporary cache now that they're not needed; failure to do so can goof up future executions in this run
|
590
709
|
clear_instantiated_variables!
|
591
710
|
|
592
|
-
|
593
|
-
content
|
711
|
+
[functions, preamble, credential_block, cim_instances_block, parameters_block, postscript].join("\n")
|
594
712
|
end
|
595
713
|
|
596
714
|
# Convert a Puppet/Ruby value into a PowerShell representation. Requires some slight additional
|
@@ -614,15 +732,16 @@ class Puppet::Provider::DscBaseProvider < Puppet::ResourceApi::SimpleProvider
|
|
614
732
|
# @param value [Object] The object to unwrap sensitive data inside of
|
615
733
|
# @return [Object] The object with any sensitive strings unwrapped and annotated
|
616
734
|
def unwrap(value)
|
617
|
-
|
735
|
+
case value
|
736
|
+
when Puppet::Pops::Types::PSensitiveType::Sensitive
|
618
737
|
"#{value.unwrap}#PuppetSensitive"
|
619
|
-
|
738
|
+
when Hash
|
620
739
|
unwrapped = {}
|
621
740
|
value.each do |k, v|
|
622
741
|
unwrapped[k] = unwrap(v)
|
623
742
|
end
|
624
743
|
unwrapped
|
625
|
-
|
744
|
+
when Array
|
626
745
|
unwrapped = []
|
627
746
|
value.each do |v|
|
628
747
|
unwrapped << unwrap(v)
|
@@ -68,6 +68,10 @@ Function ConvertTo-CanonicalResult {
|
|
68
68
|
$Value = $null
|
69
69
|
}
|
70
70
|
}
|
71
|
+
elseif ($Result.$PropertyName.GetType().Name -match 'DateTime') {
|
72
|
+
# Handle DateTimes especially since they're an edge case
|
73
|
+
$Value = Get-Date $Result.$PropertyName -UFormat "%Y-%m-%dT%H:%M:%S%Z"
|
74
|
+
}
|
71
75
|
else {
|
72
76
|
# Looks like a nested CIM instance, recurse if we're not too deep in already.
|
73
77
|
$RecursionLevel++
|
data/lib/pwsh.rb
CHANGED
@@ -14,10 +14,10 @@ require 'logger'
|
|
14
14
|
module Pwsh
|
15
15
|
# Standard errors
|
16
16
|
class Error < StandardError; end
|
17
|
+
|
17
18
|
# Create an instance of a PowerShell host and manage execution of PowerShell code inside that host.
|
18
19
|
class Manager
|
19
|
-
attr_reader :powershell_command
|
20
|
-
attr_reader :powershell_arguments
|
20
|
+
attr_reader :powershell_command, :powershell_arguments
|
21
21
|
|
22
22
|
# We actually want this to be a class variable.
|
23
23
|
@@instances = {} # rubocop:disable Style/ClassVars
|
@@ -70,7 +70,7 @@ module Pwsh
|
|
70
70
|
def self.win32console_enabled?
|
71
71
|
@win32console_enabled ||= defined?(Win32) &&
|
72
72
|
defined?(Win32::Console) &&
|
73
|
-
Win32::Console.
|
73
|
+
Win32::Console.instance_of?(Class)
|
74
74
|
end
|
75
75
|
|
76
76
|
# TODO: This thing isn't called anywhere and the variable it sets is never referenced...
|
data/lib/pwsh/util.rb
CHANGED
@@ -117,16 +117,16 @@ module Pwsh
|
|
117
117
|
#
|
118
118
|
# @return [String] representation of the value for interpolation
|
119
119
|
def format_powershell_value(object)
|
120
|
-
if %i[true false].include?(object) || %w[trueclass falseclass].include?(object.class.name.downcase)
|
120
|
+
if %i[true false].include?(object) || %w[trueclass falseclass].include?(object.class.name.downcase)
|
121
121
|
"$#{object}"
|
122
|
-
elsif object.
|
122
|
+
elsif object.instance_of?(Symbol) || object.class.ancestors.include?(Numeric)
|
123
123
|
object.to_s
|
124
|
-
elsif object.
|
124
|
+
elsif object.instance_of?(String)
|
125
125
|
"'#{escape_quotes(object)}'"
|
126
|
-
elsif object.
|
127
|
-
|
128
|
-
elsif object.
|
129
|
-
|
126
|
+
elsif object.instance_of?(Array)
|
127
|
+
"@(#{object.collect { |item| format_powershell_value(item) }.join(', ')})"
|
128
|
+
elsif object.instance_of?(Hash)
|
129
|
+
"@{#{object.collect { |k, v| "#{format_powershell_value(k)} = #{format_powershell_value(v)}" }.join('; ')}}"
|
130
130
|
else
|
131
131
|
raise "unsupported type #{object.class} of value '#{object}'"
|
132
132
|
end
|
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.7.0",
|
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/blob/
|
8
|
+
"project_page": "https://github.com/puppetlabs/ruby-pwsh/blob/main/pwshlib.md",
|
9
9
|
"issues_url": "https://github.com/puppetlabs/ruby-pwsh/issues",
|
10
10
|
"dependencies": [
|
11
11
|
|
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.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet, Inc.
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-20 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: PowerShell code manager for ruby.
|
14
14
|
email:
|
@@ -54,7 +54,7 @@ metadata:
|
|
54
54
|
homepage_uri: https://github.com/puppetlabs/ruby-pwsh
|
55
55
|
source_code_uri: https://github.com/puppetlabs/ruby-pwsh
|
56
56
|
changelog_uri: https://github.com/puppetlabs/ruby-pwsh
|
57
|
-
post_install_message:
|
57
|
+
post_install_message:
|
58
58
|
rdoc_options: []
|
59
59
|
require_paths:
|
60
60
|
- lib
|
@@ -69,9 +69,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
69
|
- !ruby/object:Gem::Version
|
70
70
|
version: '0'
|
71
71
|
requirements: []
|
72
|
-
|
73
|
-
|
74
|
-
signing_key:
|
72
|
+
rubygems_version: 3.1.4
|
73
|
+
signing_key:
|
75
74
|
specification_version: 4
|
76
75
|
summary: PowerShell code manager for ruby.
|
77
76
|
test_files: []
|