ruby-pwsh 1.0.1 → 1.1.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.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ac282abeea132104fda1b3fc2f4743f4b99603cdd6b73a42b7063641c9e65d9
|
4
|
+
data.tar.gz: 410bc1d7a7b08366a89019324b172219102197f8aa6891bcfaa1729e3bd76925
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0fd8c7af3f87ce61121a7404b66cfae5cf78fea8bdb885dc7715467bb5092bd974a9fd4d7f94824fe10675d56b4e4dc7b439826f95bc5d9672f14fcb74692501
|
7
|
+
data.tar.gz: 198328e45920aa2794cab2984e090c14c81ce56b4b038e4ae0b7f22ad66d327d0bbd34b5726709108c14609324dba1687a83a2f67026a2d8abf12d749067ca27
|
data/.rubocop.yml
CHANGED
@@ -5,7 +5,7 @@ require 'ruby-pwsh'
|
|
5
5
|
require 'pathname'
|
6
6
|
require 'json'
|
7
7
|
|
8
|
-
class Puppet::Provider::DscBaseProvider
|
8
|
+
class Puppet::Provider::DscBaseProvider # rubocop:disable Metrics/ClassLength
|
9
9
|
# Initializes the provider, preparing the instance variables which cache:
|
10
10
|
# - the canonicalized resources across calls
|
11
11
|
# - query results
|
@@ -44,6 +44,7 @@ class Puppet::Provider::DscBaseProvider
|
|
44
44
|
# @param context [Object] the Puppet runtime context to operate in and send feedback to
|
45
45
|
# @param resources [Hash] the hash of the resource to canonicalize from either manifest or invocation
|
46
46
|
# @return [Hash] returns a hash representing the current state of the object, if it exists
|
47
|
+
# rubocop:disable Metrics/BlockLength, Metrics/MethodLength
|
47
48
|
def canonicalize(context, resources)
|
48
49
|
canonicalized_resources = []
|
49
50
|
resources.collect do |r|
|
@@ -83,9 +84,18 @@ class Puppet::Provider::DscBaseProvider
|
|
83
84
|
downcased_result.each do |key, value|
|
84
85
|
# Canonicalize to the manifest value unless the downcased strings match and the attribute is not an enum:
|
85
86
|
# - When the values don't match at all, the manifest value is desired;
|
86
|
-
# - When the values match case insensitively but the attribute is an enum,
|
87
|
-
#
|
88
|
-
|
87
|
+
# - When the values match case insensitively but the attribute is an enum, and the casing from invoke_get_method
|
88
|
+
# is not int the enum, prefer the casing of the manifest enum.
|
89
|
+
# - When the values match case insensitively and the attribute is not an enum, or is an enum and invoke_get_method casing
|
90
|
+
# is in the enum, prefer the casing from invoke_get_method
|
91
|
+
is_enum = enum_attributes(context).include?(key)
|
92
|
+
canonicalized_value_in_enum = if is_enum
|
93
|
+
enum_values(context, key).include?(canonicalized[key])
|
94
|
+
else
|
95
|
+
false
|
96
|
+
end
|
97
|
+
match_insensitively = same?(value, downcased_resource[key])
|
98
|
+
canonicalized[key] = r[key] unless match_insensitively && (canonicalized_value_in_enum || !is_enum)
|
89
99
|
canonicalized.delete(key) unless downcased_resource.key?(key)
|
90
100
|
end
|
91
101
|
# Cache the actually canonicalized resource separately
|
@@ -104,6 +114,7 @@ class Puppet::Provider::DscBaseProvider
|
|
104
114
|
context.debug("Canonicalized Resources: #{canonicalized_resources}")
|
105
115
|
canonicalized_resources
|
106
116
|
end
|
117
|
+
# rubocop:enable Metrics/BlockLength, Metrics/MethodLength
|
107
118
|
|
108
119
|
# Attempts to retrieve an instance of the DSC resource, invoking the `Get` method and passing any
|
109
120
|
# namevars as the Properties to Invoke-DscResource. The result object, if any, is compared to the
|
@@ -244,6 +255,7 @@ class Puppet::Provider::DscBaseProvider
|
|
244
255
|
script_content = ps_script_content(resource)
|
245
256
|
context.debug("Script:\n #{redact_secrets(script_content)}")
|
246
257
|
output = ps_manager.execute(remove_secret_identifiers(script_content))[:stdout]
|
258
|
+
|
247
259
|
if output.nil?
|
248
260
|
context.err('Nothing returned')
|
249
261
|
return nil
|
@@ -256,8 +268,10 @@ class Puppet::Provider::DscBaseProvider
|
|
256
268
|
return nil
|
257
269
|
end
|
258
270
|
context.debug("raw data received: #{data.inspect}")
|
271
|
+
collision_error_matcher = /The Invoke-DscResource cmdlet is in progress and must return before Invoke-DscResource can be invoked/
|
259
272
|
|
260
273
|
error = data['errormessage']
|
274
|
+
|
261
275
|
unless error.nil? || error.empty?
|
262
276
|
# NB: We should have a way to stop processing this resource *now* without blowing up the whole Puppet run
|
263
277
|
# Raising an error stops processing but blows things up while context.err alerts but continues to process
|
@@ -267,6 +281,11 @@ class Puppet::Provider::DscBaseProvider
|
|
267
281
|
@logon_failures << name_hash[:dsc_psdscrunascredential].dup
|
268
282
|
# This is a hack to handle the query cache to prevent a second lookup
|
269
283
|
@cached_query_results << name_hash # if fetch_cached_hashes(@cached_query_results, [data]).empty?
|
284
|
+
elsif error.match?(collision_error_matcher)
|
285
|
+
context.notice('Invoke-DscResource collision detected: Please stagger the timing of your Puppet runs as this can lead to unexpected behaviour.')
|
286
|
+
retry_invoke_dsc_resource(context, 5, 60, collision_error_matcher) do
|
287
|
+
data = ps_manager.execute(remove_secret_identifiers(script_content))[:stdout]
|
288
|
+
end
|
270
289
|
else
|
271
290
|
context.err(error)
|
272
291
|
end
|
@@ -276,6 +295,35 @@ class Puppet::Provider::DscBaseProvider
|
|
276
295
|
data
|
277
296
|
end
|
278
297
|
|
298
|
+
# Retries Invoke-DscResource when returned error matches error regex supplied as param.
|
299
|
+
# @param context [Object] the Puppet runtime context to operate in and send feedback to
|
300
|
+
# @param max_retry_count [Int] max number of times to retry Invoke-DscResource
|
301
|
+
# @param retry_wait_interval_secs [Int] Time delay between retries
|
302
|
+
# @param error_matcher [String] the regex pattern to match with error
|
303
|
+
def retry_invoke_dsc_resource(context, max_retry_count, retry_wait_interval_secs, error_matcher)
|
304
|
+
try = 0
|
305
|
+
while try < max_retry_count
|
306
|
+
try += 1
|
307
|
+
# notify and wait for retry interval
|
308
|
+
context.notice("Sleeping for #{retry_wait_interval_secs} seconds.")
|
309
|
+
sleep retry_wait_interval_secs
|
310
|
+
# notify and retry
|
311
|
+
context.notice("Retrying: attempt #{try} of #{max_retry_count}.")
|
312
|
+
data = JSON.parse(yield)
|
313
|
+
# if no error, assume successful invocation and break
|
314
|
+
break if data['errormessage'].nil?
|
315
|
+
|
316
|
+
# notify of failed retry
|
317
|
+
context.notice("Attempt #{try} of #{max_retry_count} failed.")
|
318
|
+
# return if error does not match expceted error, or all retries exhausted
|
319
|
+
return context.err(data['errormessage']) unless data['errormessage'].match?(error_matcher) && try < max_retry_count
|
320
|
+
|
321
|
+
# else, retry
|
322
|
+
next
|
323
|
+
end
|
324
|
+
data
|
325
|
+
end
|
326
|
+
|
279
327
|
# Determine if the DSC Resource is in the desired state, invoking the `Test` method unless it's
|
280
328
|
# already been run for the resource, in which case reuse the result instead of checking for each
|
281
329
|
# property. This behavior is only triggered if the validation_mode is set to resource; by default
|
@@ -303,7 +351,7 @@ class Puppet::Provider::DscBaseProvider
|
|
303
351
|
# @param context [Object] the Puppet runtime context to operate in and send feedback to
|
304
352
|
# @param name_hash [Hash] the hash of namevars to be passed as properties to `Invoke-DscResource`
|
305
353
|
# @return [Hash] returns a hash representing the DSC resource munged to the representation the Puppet Type expects
|
306
|
-
def invoke_get_method(context, name_hash)
|
354
|
+
def invoke_get_method(context, name_hash) # rubocop:disable Metrics/AbcSize
|
307
355
|
context.debug("retrieving #{name_hash.inspect}")
|
308
356
|
|
309
357
|
query_props = name_hash.select { |k, v| mandatory_get_attributes(context).include?(k) || (k == :dsc_psdscrunascredential && !v.nil?) }
|
@@ -346,6 +394,8 @@ class Puppet::Provider::DscBaseProvider
|
|
346
394
|
# If a resource is found, it's present, so refill this Puppet-only key
|
347
395
|
data[:name] = name_hash[:name]
|
348
396
|
|
397
|
+
data = stringify_nil_attributes(context, data)
|
398
|
+
|
349
399
|
# Have to check for this to avoid a weird canonicalization warning
|
350
400
|
# The Resource API calls canonicalize against the current state which
|
351
401
|
# will lead to dsc_ensure being set to absent in the name_hash even if
|
@@ -615,6 +665,20 @@ class Puppet::Provider::DscBaseProvider
|
|
615
665
|
context.type.attributes.select { |_attribute, properties| properties[:mandatory_for_set] }.keys
|
616
666
|
end
|
617
667
|
|
668
|
+
# Parses the DSC resource type definition to retrieve the names of any attributes which are specifed as required strings
|
669
|
+
# This is used to ensure that any nil values are converted to empty strings to match puppets expecetd value
|
670
|
+
# @param context [Object] the Puppet runtime context to operate in and send feedback to
|
671
|
+
# @param data [Hash] the hash of properties returned from the DSC resource
|
672
|
+
# @return [Hash] returns a data hash with any nil values converted to empty strings
|
673
|
+
def stringify_nil_attributes(context, data)
|
674
|
+
nil_strings = data.select { |_name, value| value.nil? }.keys
|
675
|
+
string_attrs = context.type.attributes.select { |_name, properties| properties[:type] == 'String' }.keys
|
676
|
+
string_attrs.each do |attribute|
|
677
|
+
data[attribute] = '' if nil_strings.include?(attribute)
|
678
|
+
end
|
679
|
+
data
|
680
|
+
end
|
681
|
+
|
618
682
|
# Parses the DSC resource type definition to retrieve the names of any attributes which are specified as namevars
|
619
683
|
#
|
620
684
|
# @param context [Object] the Puppet runtime context to operate in and send feedback to
|
@@ -641,6 +705,28 @@ class Puppet::Provider::DscBaseProvider
|
|
641
705
|
context.type.attributes.select { |_name, properties| properties[:type].include?('Enum[') }.keys
|
642
706
|
end
|
643
707
|
|
708
|
+
# Parses the DSC resource type definition to retrieve the values of any attributes which are specified as enums
|
709
|
+
#
|
710
|
+
# @param context [Object] the Puppet runtime context to operate in and send feedback to
|
711
|
+
# @param attribute [String] the enum attribute to retrieve the allowed values from
|
712
|
+
# @return [Array] returns an array of attribute names as symbols which are enums
|
713
|
+
def enum_values(context, attribute)
|
714
|
+
# Get the attribute's type string for the given key
|
715
|
+
type_string = context.type.attributes[attribute][:type]
|
716
|
+
|
717
|
+
# Return an empty array if the key doesn't have an Enum type or doesn't exist
|
718
|
+
return [] unless type_string&.include?('Enum[')
|
719
|
+
|
720
|
+
# Extract the enum values from the type string
|
721
|
+
enum_content = type_string.match(/Enum\[(.*?)\]/)&.[](1)
|
722
|
+
|
723
|
+
# Return an empty array if we couldn't find the enum values
|
724
|
+
return [] if enum_content.nil?
|
725
|
+
|
726
|
+
# Return an array of the enum values, stripped of extra whitespace and quote marks
|
727
|
+
enum_content.split(',').map { |val| val.strip.delete('\'') }
|
728
|
+
end
|
729
|
+
|
644
730
|
# Look through a fully formatted string, replacing all instances where a value matches the formatted properties
|
645
731
|
# of an instantiated variable with references to the variable instead. This allows us to pass complex and nested
|
646
732
|
# CIM instances to the Invoke-DscResource parameter hash without constructing them *in* the hash.
|
@@ -666,7 +752,7 @@ class Puppet::Provider::DscBaseProvider
|
|
666
752
|
# @param resource [Hash] a hash with the information needed to run `Invoke-DscResource`
|
667
753
|
# @return [String] A multi-line string which sets the PSModulePath at the system level
|
668
754
|
def munge_psmodulepath(resource)
|
669
|
-
vendor_path = resource[:vendored_modules_path]
|
755
|
+
vendor_path = resource[:vendored_modules_path]&.tr('/', '\\')
|
670
756
|
<<~MUNGE_PSMODULEPATH.strip
|
671
757
|
$UnmungedPSModulePath = [System.Environment]::GetEnvironmentVariable('PSModulePath','machine')
|
672
758
|
$MungedPSModulePath = $env:PSModulePath + ';#{vendor_path}'
|
data/lib/pwsh/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
if ENV['COVERAGE'] == 'yes'
|
4
|
+
begin
|
5
|
+
require 'simplecov'
|
6
|
+
require 'simplecov-console'
|
7
|
+
|
8
|
+
SimpleCov.formatters = [
|
9
|
+
SimpleCov::Formatter::HTMLFormatter,
|
10
|
+
SimpleCov::Formatter::Console
|
11
|
+
]
|
12
|
+
|
13
|
+
SimpleCov.start do
|
14
|
+
track_files 'lib/**/*.rb'
|
15
|
+
|
16
|
+
add_filter '/spec'
|
17
|
+
add_filter 'lib/pwsh/version.rb'
|
18
|
+
|
19
|
+
# do not track vendored files
|
20
|
+
add_filter '/vendor'
|
21
|
+
add_filter '/.vendor'
|
22
|
+
end
|
23
|
+
rescue LoadError
|
24
|
+
raise 'Add the simplecov & simplecov-console gems to Gemfile to enable this task'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
3
28
|
require 'bundler/setup'
|
4
29
|
require 'ruby-pwsh'
|
5
30
|
|
@@ -2,23 +2,24 @@
|
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
require 'puppet/type'
|
5
|
+
require 'puppet/resource_api'
|
5
6
|
require 'puppet/provider/dsc_base_provider/dsc_base_provider'
|
6
7
|
require 'json'
|
7
8
|
|
8
9
|
RSpec.describe Puppet::Provider::DscBaseProvider do
|
9
10
|
subject(:provider) { described_class.new }
|
10
11
|
|
11
|
-
let(:context) { instance_double(Puppet::ResourceApi::
|
12
|
-
let(:type) { instance_double(Puppet::ResourceApi::TypeDefinition) }
|
12
|
+
let(:context) { instance_double(Puppet::ResourceApi::BaseContext, 'context') }
|
13
|
+
let(:type) { instance_double(Puppet::ResourceApi::TypeDefinition, 'typedef') }
|
13
14
|
let(:ps_manager) { instance_double(Pwsh::Manager) }
|
14
15
|
let(:execute_response) { { stdout: nil, stderr: nil, exitcode: 0 } }
|
15
16
|
|
16
17
|
# Reset the caches after each run
|
17
18
|
after do
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
provider.instance_variable_set(:@cached_canonicalized_resource, [])
|
20
|
+
provider.instance_variable_set(:@cached_query_results, [])
|
21
|
+
provider.instance_variable_set(:@cached_test_results, [])
|
22
|
+
provider.instance_variable_set(:@logon_failures, [])
|
22
23
|
end
|
23
24
|
|
24
25
|
describe '.initialize' do
|
@@ -28,27 +29,30 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
28
29
|
end
|
29
30
|
|
30
31
|
it 'initializes the cached_canonicalized_resource instance variable' do
|
31
|
-
expect(
|
32
|
+
expect(provider.instance_variable_get(:@cached_canonicalized_resource)).to eq([])
|
32
33
|
end
|
33
34
|
|
34
35
|
it 'initializes the cached_query_results instance variable' do
|
35
|
-
expect(
|
36
|
+
expect(provider.instance_variable_get(:@cached_query_results)).to eq([])
|
36
37
|
end
|
37
38
|
|
38
39
|
it 'initializes the cached_test_results instance variable' do
|
39
|
-
expect(
|
40
|
+
expect(provider.instance_variable_get(:@cached_test_results)).to eq([])
|
40
41
|
end
|
41
42
|
|
42
43
|
it 'initializes the logon_failures instance variable' do
|
43
|
-
expect(
|
44
|
+
expect(provider.instance_variable_get(:@logon_failures)).to eq([])
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
47
48
|
describe '.cached_test_results' do
|
48
49
|
let(:cache_value) { %w[foo bar] }
|
49
50
|
|
51
|
+
before do
|
52
|
+
provider.instance_variable_set(:@cached_test_results, cache_value)
|
53
|
+
end
|
54
|
+
|
50
55
|
it 'returns the value of the @cached_test_results instance variable' do
|
51
|
-
described_class.instance_variable_set(:@cached_test_results, cache_value)
|
52
56
|
expect(provider.cached_test_results).to eq(cache_value)
|
53
57
|
end
|
54
58
|
end
|
@@ -175,6 +179,8 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
175
179
|
end
|
176
180
|
|
177
181
|
it 'treats the manifest value as canonical' do
|
182
|
+
expect(context).to receive(:type).and_return(type)
|
183
|
+
expect(type).to receive(:attributes).and_return({ dsc_property: { type: "Enum['Dword']" } })
|
178
184
|
expect(canonicalized_resource.first[:dsc_property]).to eq('Dword')
|
179
185
|
end
|
180
186
|
end
|
@@ -237,11 +243,11 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
237
243
|
|
238
244
|
describe '.get' do
|
239
245
|
after do
|
240
|
-
|
246
|
+
provider.instance_variable_set(:@cached_canonicalized_resource, [])
|
241
247
|
end
|
242
248
|
|
243
249
|
it 'checks the cached results, returning if one exists for the specified names' do
|
244
|
-
|
250
|
+
provider.instance_variable_set(:@cached_canonicalized_resource, [])
|
245
251
|
allow(context).to receive(:debug)
|
246
252
|
expect(provider).to receive(:fetch_cached_hashes).with([], [{ name: 'foo' }]).and_return([{ name: 'foo', property: 'bar' }])
|
247
253
|
expect(provider).not_to receive(:invoke_get_method)
|
@@ -249,7 +255,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
249
255
|
end
|
250
256
|
|
251
257
|
it 'adds mandatory properties to the name hash when calling invoke_get_method' do
|
252
|
-
|
258
|
+
provider.instance_variable_set(:@cached_canonicalized_resource, [{ name: 'foo', property: 'bar', dsc_some_parameter: 'baz' }])
|
253
259
|
allow(context).to receive(:debug)
|
254
260
|
expect(provider).to receive(:fetch_cached_hashes).with([], [{ name: 'foo' }]).and_return([])
|
255
261
|
expect(provider).to receive(:namevar_attributes).and_return([:name]).exactly(3).times
|
@@ -530,7 +536,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
530
536
|
end
|
531
537
|
|
532
538
|
after do
|
533
|
-
|
539
|
+
provider.instance_variable_set(:@cached_query_results, [])
|
534
540
|
end
|
535
541
|
|
536
542
|
context 'when the invocation script returns data without errors' do
|
@@ -557,7 +563,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
557
563
|
|
558
564
|
it 'caches the result' do
|
559
565
|
expect { result }.not_to raise_error
|
560
|
-
expect(
|
566
|
+
expect(provider.instance_variable_get(:@cached_query_results)).to eq([result])
|
561
567
|
end
|
562
568
|
|
563
569
|
it 'removes unrelated properties from the result' do
|
@@ -719,7 +725,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
719
725
|
end
|
720
726
|
|
721
727
|
after do
|
722
|
-
|
728
|
+
provider.instance_variable_set(:@logon_failures, [])
|
723
729
|
end
|
724
730
|
|
725
731
|
it 'errors specifically for a logon failure and returns nil' do
|
@@ -728,12 +734,12 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
728
734
|
|
729
735
|
it 'caches the logon failure' do
|
730
736
|
expect { result }.not_to raise_error
|
731
|
-
expect(
|
737
|
+
expect(provider.instance_variable_get(:@logon_failures)).to eq([credential_hash])
|
732
738
|
end
|
733
739
|
|
734
740
|
it 'caches the query results' do
|
735
741
|
expect { result }.not_to raise_error
|
736
|
-
expect(
|
742
|
+
expect(provider.instance_variable_get(:@cached_query_results)).to eq([name_hash])
|
737
743
|
end
|
738
744
|
end
|
739
745
|
|
@@ -790,6 +796,45 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
790
796
|
end
|
791
797
|
end
|
792
798
|
|
799
|
+
context 'when the invocation script errors with a collision' do
|
800
|
+
it 'writes a notice via context and applies successfully on retry' do
|
801
|
+
expect(ps_manager).to receive(:execute).and_return({ stdout: '{"errormessage": "The Invoke-DscResource cmdlet is in progress and must return before Invoke-DscResource can be invoked"}' })
|
802
|
+
expect(context).to receive(:notice).with(/Invoke-DscResource collision detected: Please stagger the timing of your Puppet runs as this can lead to unexpected behaviour./).once
|
803
|
+
expect(context).to receive(:notice).with('Sleeping for 60 seconds.').twice
|
804
|
+
expect(context).to receive(:notice).with(/Retrying: attempt [1-2] of 5/).twice
|
805
|
+
expect(ps_manager).to receive(:execute).and_return({ stdout: '{"errormessage": "The Invoke-DscResource cmdlet is in progress and must return before Invoke-DscResource can be invoked"}' })
|
806
|
+
expect(context).to receive(:notice).with('Attempt 1 of 5 failed.')
|
807
|
+
allow(provider).to receive(:sleep)
|
808
|
+
expect(ps_manager).to receive(:execute).and_return({ stdout: '{"errormessage": null}' })
|
809
|
+
expect { result }.not_to raise_error
|
810
|
+
end
|
811
|
+
|
812
|
+
it 'writes a error via context and fails to apply when all retry attempts used' do
|
813
|
+
expect(ps_manager).to receive(:execute).and_return({ stdout: '{"errormessage": "The Invoke-DscResource cmdlet is in progress and must return before Invoke-DscResource can be invoked"}' })
|
814
|
+
.exactly(5).times
|
815
|
+
expect(context).to receive(:notice).with(/Invoke-DscResource collision detected: Please stagger the timing of your Puppet runs as this can lead to unexpected behaviour./).once
|
816
|
+
expect(context).to receive(:notice).with('Sleeping for 60 seconds.').exactly(5).times
|
817
|
+
expect(context).to receive(:notice).with(/Retrying: attempt [1-5] of 5/).exactly(5).times
|
818
|
+
expect(ps_manager).to receive(:execute).and_return({ stdout: '{"errormessage": "The Invoke-DscResource cmdlet is in progress and must return before Invoke-DscResource can be invoked"}' })
|
819
|
+
expect(context).to receive(:notice).with(/Attempt [1-5] of 5 failed/).exactly(5).times
|
820
|
+
expect(context).to receive(:err).with(/The Invoke-DscResource cmdlet is in progress and must return before Invoke-DscResource can be invoked/)
|
821
|
+
allow(provider).to receive(:sleep)
|
822
|
+
expect(result).to be_nil
|
823
|
+
end
|
824
|
+
|
825
|
+
it 'writes an error via context and fails to apply when encountering an unexpected error' do
|
826
|
+
expect(ps_manager).to receive(:execute).and_return({ stdout: '{"errormessage": "The Invoke-DscResource cmdlet is in progress and must return before Invoke-DscResource can be invoked"}' })
|
827
|
+
expect(context).to receive(:notice).with(/Invoke-DscResource collision detected: Please stagger the timing of your Puppet runs as this can lead to unexpected behaviour./).once
|
828
|
+
expect(context).to receive(:notice).with('Sleeping for 60 seconds.').once
|
829
|
+
expect(context).to receive(:notice).with(/Retrying: attempt 1 of 5/).once
|
830
|
+
allow(provider).to receive(:sleep)
|
831
|
+
expect(ps_manager).to receive(:execute).and_return({ stdout: '{"errormessage": "Some unexpected error"}' }).once
|
832
|
+
expect(context).to receive(:notice).with(/Attempt 1 of 5 failed/).once
|
833
|
+
expect(context).to receive(:err).with(/Some unexpected error/)
|
834
|
+
expect(result).to be_nil
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
793
838
|
context 'when the invocation script returns data without errors' do
|
794
839
|
it 'filters for the correct properties to invoke and returns the results' do
|
795
840
|
expect(ps_manager).to receive(:execute).with("Script: #{apply_props}").and_return({ stdout: '{"in_desired_state": true, "errormessage": null}' })
|
@@ -981,11 +1026,11 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
981
1026
|
end
|
982
1027
|
|
983
1028
|
describe '.invoke_test_method' do
|
984
|
-
subject(:result) { provider.invoke_test_method(context, name,
|
1029
|
+
subject(:result) { provider.invoke_test_method(context, name, should_hash) }
|
985
1030
|
|
986
1031
|
let(:name) { { name: 'foo', dsc_name: 'bar' } }
|
987
|
-
let(:
|
988
|
-
let(:test_properties) {
|
1032
|
+
let(:should_hash) { name.merge(dsc_ensure: 'present') }
|
1033
|
+
let(:test_properties) { should_hash.reject { |k, _v| k == :name } }
|
989
1034
|
let(:invoke_dsc_resource_data) { nil }
|
990
1035
|
|
991
1036
|
before do
|
@@ -995,7 +1040,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
995
1040
|
end
|
996
1041
|
|
997
1042
|
after do
|
998
|
-
|
1043
|
+
provider.instance_variable_set(:@cached_test_results, [])
|
999
1044
|
end
|
1000
1045
|
|
1001
1046
|
context 'when something went wrong calling Invoke-DscResource' do
|
@@ -1043,7 +1088,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
1043
1088
|
|
1044
1089
|
describe '.instantiated_variables' do
|
1045
1090
|
after do
|
1046
|
-
|
1091
|
+
provider.instance_variable_set(:@instantiated_variables, [])
|
1047
1092
|
end
|
1048
1093
|
|
1049
1094
|
it 'sets the instantiated_variables instance variable to {} if not initialized' do
|
@@ -1051,20 +1096,20 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
1051
1096
|
end
|
1052
1097
|
|
1053
1098
|
it 'returns the instantiated_variables instance variable if already initialized' do
|
1054
|
-
|
1099
|
+
provider.instance_variable_set(:@instantiated_variables, { foo: 'bar' })
|
1055
1100
|
expect(provider.instantiated_variables).to eq({ foo: 'bar' })
|
1056
1101
|
end
|
1057
1102
|
end
|
1058
1103
|
|
1059
1104
|
describe '.clear_instantiated_variables!' do
|
1060
1105
|
after do
|
1061
|
-
|
1106
|
+
provider.instance_variable_set(:@instantiated_variables, [])
|
1062
1107
|
end
|
1063
1108
|
|
1064
1109
|
it 'sets the instantiated_variables instance variable to {}' do
|
1065
|
-
|
1110
|
+
provider.instance_variable_set(:@instantiated_variables, { foo: 'bar' })
|
1066
1111
|
expect { provider.clear_instantiated_variables! }.not_to raise_error
|
1067
|
-
expect(
|
1112
|
+
expect(provider.instance_variable_get(:@instantiated_variables)).to eq({})
|
1068
1113
|
end
|
1069
1114
|
end
|
1070
1115
|
|
@@ -1087,16 +1132,16 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
1087
1132
|
end
|
1088
1133
|
|
1089
1134
|
after do
|
1090
|
-
|
1135
|
+
provider.instance_variable_set(:@logon_failures, [])
|
1091
1136
|
end
|
1092
1137
|
|
1093
1138
|
it 'returns false if there have been no failed logons with the username/password combination' do
|
1094
|
-
|
1139
|
+
provider.instance_variable_set(:@logon_failures, [bad_credential_hash])
|
1095
1140
|
expect(provider.logon_failed_already?(good_credential_hash)).to be(false)
|
1096
1141
|
end
|
1097
1142
|
|
1098
1143
|
it 'returns true if the username/password specified are found in the logon_failures instance variable' do
|
1099
|
-
|
1144
|
+
provider.instance_variable_set(:@logon_failures, [good_credential_hash, bad_credential_hash])
|
1100
1145
|
expect(provider.logon_failed_already?(bad_credential_hash)).to be(true)
|
1101
1146
|
end
|
1102
1147
|
end
|
@@ -1437,16 +1482,18 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
1437
1482
|
context 'when the resource does not have the dscmeta_resource_implementation key' do
|
1438
1483
|
let(:test_resource) { {} }
|
1439
1484
|
|
1440
|
-
it '
|
1441
|
-
|
1485
|
+
it 'sets $UnmungedPSModulePath to the current PSModulePath' do
|
1486
|
+
# since https://github.com/puppetlabs/ruby-pwsh/pull/261 we load vendored path for MOF resources as well
|
1487
|
+
expect(result).to match(/\$UnmungedPSModulePath = .+GetEnvironmentVariable.+PSModulePath.+machine/)
|
1442
1488
|
end
|
1443
1489
|
end
|
1444
1490
|
|
1445
1491
|
context "when the resource's dscmeta_resource_implementation is not 'Class'" do
|
1446
1492
|
let(:test_resource) { { dscmeta_resource_implementation: 'MOF' } }
|
1447
1493
|
|
1448
|
-
|
1449
|
-
|
1494
|
+
# since https://github.com/puppetlabs/ruby-pwsh/pull/261 we load vendored path for MOF resources as well
|
1495
|
+
it 'sets $UnmungedPSModulePath to the current PSModulePath' do
|
1496
|
+
expect(result).to match(/\$UnmungedPSModulePath = .+GetEnvironmentVariable.+PSModulePath.+machine/)
|
1450
1497
|
end
|
1451
1498
|
end
|
1452
1499
|
|
@@ -1510,7 +1557,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
1510
1557
|
end
|
1511
1558
|
|
1512
1559
|
after do
|
1513
|
-
|
1560
|
+
provider.instance_variable_set(:@instantiated_variables, [])
|
1514
1561
|
end
|
1515
1562
|
|
1516
1563
|
it 'writes the ruby representation of the credentials as the value of a key named for the new variable into the instantiated_variables cache' do
|
@@ -1543,7 +1590,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
1543
1590
|
subject(:result) { provider.prepare_cim_instances(test_resource) }
|
1544
1591
|
|
1545
1592
|
after do
|
1546
|
-
|
1593
|
+
provider.instance_variable_set(:@instantiated_variables, [])
|
1547
1594
|
end
|
1548
1595
|
|
1549
1596
|
context 'when a cim instance is passed without nested cim instances' do
|
@@ -1652,7 +1699,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
1652
1699
|
|
1653
1700
|
describe '.format_ciminstance' do
|
1654
1701
|
after do
|
1655
|
-
|
1702
|
+
provider.instance_variable_set(:@instantiated_variables, [])
|
1656
1703
|
end
|
1657
1704
|
|
1658
1705
|
it 'defines and returns a new cim instance as a PowerShell variable, passing the class name and property hash' do
|
@@ -1668,7 +1715,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
1668
1715
|
end
|
1669
1716
|
|
1670
1717
|
it 'interpolates variables in the case of a cim instance containing a nested instance' do
|
1671
|
-
|
1718
|
+
provider.instance_variable_set(:@instantiated_variables, { 'SomeVariable' => { 'bar' => 'ope' } })
|
1672
1719
|
property_hash = { 'foo' => { 'bar' => 'ope' } }
|
1673
1720
|
expect(provider.format_ciminstance('foo', 'SomeClass', property_hash)).to match(/@\{'foo' = \$SomeVariable\}/)
|
1674
1721
|
end
|
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: 1.
|
4
|
+
version: 1.1.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: 2024-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: PowerShell code manager for ruby.
|
14
14
|
email:
|