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