ruby-pwsh 0.5.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|