ruby-pwsh 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -0
- data/lib/puppet/provider/dsc_base_provider/dsc_base_provider.rb +32 -11
- data/lib/puppet/provider/dsc_base_provider/invoke_dsc_resource_functions.ps1 +1 -1
- data/lib/pwsh/util.rb +3 -3
- data/lib/pwsh/version.rb +1 -1
- data/lib/pwsh.rb +1 -1
- data/spec/unit/puppet/provider/dsc_base_provider/dsc_base_provider_spec.rb +62 -19
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e217873cdddb9b32ba467e0a9f5de6977c04b11a1c0788613dda6181e96a62bd
|
4
|
+
data.tar.gz: 8dc82dfad8869d7c632ba3d902b4e22c375aeb5988f84e359269163da885b604
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a7e02f1cee48fa49316c133268a16f9bc3b8f195927834854c81eee134cdb7056710cd1be04eaf992ca036aa2b014175d06ebf8684eef6354ef8d10a07a62e31
|
7
|
+
data.tar.gz: da78f276002b67faf3716ce0b8157fe3cf0edbbeb5da3ae1145c3ed696e386838285138fdd0c75b8a592b677b7631c66ce07355a25788dee58354af0ecdd9eaa
|
data/README.md
CHANGED
@@ -86,6 +86,10 @@ The following platforms are supported:
|
|
86
86
|
- RedHat
|
87
87
|
- Ubuntu
|
88
88
|
|
89
|
+
## Limitations
|
90
|
+
|
91
|
+
- When PowerShell [Script Block Logging](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_logging_windows?view=powershell-7.4#enabling-script-block-logging) is enabled, data marked as sensitive in your manifest may appear in these logs as plain text. It is **highly recommended**, by both Puppet and Microsoft, that you also enable [Protected Event Logging](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_logging_windows?view=powershell-7.4#protected-event-logging) alongside this to encrypt the logs to protect this information.
|
92
|
+
|
89
93
|
## License
|
90
94
|
|
91
95
|
This codebase is licensed under Apache 2.0. However, the open source dependencies included in this codebase might be subject to other software licenses such as AGPL, GPL2.0, and MIT.
|
@@ -15,6 +15,7 @@ class Puppet::Provider::DscBaseProvider # rubocop:disable Metrics/ClassLength
|
|
15
15
|
@cached_query_results = []
|
16
16
|
@cached_test_results = []
|
17
17
|
@logon_failures = []
|
18
|
+
@timeout = nil # default timeout, ps_manager.execute is expecting nil by default..
|
18
19
|
super
|
19
20
|
end
|
20
21
|
|
@@ -251,18 +252,22 @@ class Puppet::Provider::DscBaseProvider # rubocop:disable Metrics/ClassLength
|
|
251
252
|
context.err('Logon credentials are invalid')
|
252
253
|
return nil
|
253
254
|
end
|
255
|
+
specify_dsc_timeout(name_hash)
|
254
256
|
resource = invocable_resource(props, context, method)
|
255
257
|
script_content = ps_script_content(resource)
|
258
|
+
context.debug("Invoke-DSC Timeout: #{@timeout} milliseconds") if @timeout
|
256
259
|
context.debug("Script:\n #{redact_secrets(script_content)}")
|
257
|
-
output = ps_manager.execute(remove_secret_identifiers(script_content))
|
260
|
+
output = ps_manager.execute(remove_secret_identifiers(script_content), @timeout)
|
258
261
|
|
259
|
-
if output.nil?
|
260
|
-
|
262
|
+
if output[:stdout].nil?
|
263
|
+
message = 'Nothing returned.'
|
264
|
+
message += " #{output[:errormessage]}" if output[:errormessage]&.match?(/PowerShell module timeout \(\d+ ms\) exceeded while executing/)
|
265
|
+
context.err(message)
|
261
266
|
return nil
|
262
267
|
end
|
263
268
|
|
264
269
|
begin
|
265
|
-
data = JSON.parse(output)
|
270
|
+
data = JSON.parse(output[:stdout])
|
266
271
|
rescue StandardError => e
|
267
272
|
context.err(e)
|
268
273
|
return nil
|
@@ -295,6 +300,18 @@ class Puppet::Provider::DscBaseProvider # rubocop:disable Metrics/ClassLength
|
|
295
300
|
data
|
296
301
|
end
|
297
302
|
|
303
|
+
# Sets the @timeout instance variable.
|
304
|
+
# @param name_hash [Hash] the hash of namevars to be passed as properties to `Invoke-DscResource`
|
305
|
+
# The @timeout variable is set to the value of name_hash[:dsc_timeout] in milliseconds
|
306
|
+
# If name_hash[:dsc_timeout] is nil, @timeout is not changed.
|
307
|
+
# If @timeout is already set to a value other than nil,
|
308
|
+
# it is changed only if it's different from name_hash[:dsc_timeout]..
|
309
|
+
def specify_dsc_timeout(name_hash)
|
310
|
+
return unless name_hash[:dsc_timeout] && (@timeout.nil? || @timeout != name_hash[:dsc_timeout])
|
311
|
+
|
312
|
+
@timeout = name_hash[:dsc_timeout] * 1000
|
313
|
+
end
|
314
|
+
|
298
315
|
# Retries Invoke-DscResource when returned error matches error regex supplied as param.
|
299
316
|
# @param context [Object] the Puppet runtime context to operate in and send feedback to
|
300
317
|
# @param max_retry_count [Int] max number of times to retry Invoke-DscResource
|
@@ -665,16 +682,16 @@ class Puppet::Provider::DscBaseProvider # rubocop:disable Metrics/ClassLength
|
|
665
682
|
context.type.attributes.select { |_attribute, properties| properties[:mandatory_for_set] }.keys
|
666
683
|
end
|
667
684
|
|
668
|
-
# Parses the DSC resource type definition to retrieve the names of any attributes which are
|
669
|
-
# This is used to ensure that any nil values are converted to empty strings to match puppets
|
685
|
+
# Parses the DSC resource type definition to retrieve the names of any attributes which are specified as required strings
|
686
|
+
# This is used to ensure that any nil values are converted to empty strings to match puppets expected value
|
670
687
|
# @param context [Object] the Puppet runtime context to operate in and send feedback to
|
671
688
|
# @param data [Hash] the hash of properties returned from the DSC resource
|
672
689
|
# @return [Hash] returns a data hash with any nil values converted to empty strings
|
673
690
|
def stringify_nil_attributes(context, data)
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
data[
|
691
|
+
nil_attributes = data.select { |_name, value| value.nil? }.keys
|
692
|
+
nil_attributes.each do |nil_attr|
|
693
|
+
attribute_type = context.type.attributes[nil_attr][:type]
|
694
|
+
data[nil_attr] = '' if (attribute_type.include?('Enum[') && enum_values(context, nil_attr).include?('')) || attribute_type == 'String'
|
678
695
|
end
|
679
696
|
data
|
680
697
|
end
|
@@ -1076,6 +1093,10 @@ class Puppet::Provider::DscBaseProvider # rubocop:disable Metrics/ClassLength
|
|
1076
1093
|
def ps_manager
|
1077
1094
|
debug_output = Puppet::Util::Log.level == :debug
|
1078
1095
|
# TODO: Allow you to specify an alternate path, either to pwsh generally or a specific pwsh path.
|
1079
|
-
|
1096
|
+
if Pwsh::Util.on_windows?
|
1097
|
+
Pwsh::Manager.instance(Pwsh::Manager.powershell_path, Pwsh::Manager.powershell_args, debug: debug_output)
|
1098
|
+
else
|
1099
|
+
Pwsh::Manager.instance(Pwsh::Manager.pwsh_path, Pwsh::Manager.pwsh_args, debug: debug_output)
|
1100
|
+
end
|
1080
1101
|
end
|
1081
1102
|
end
|
data/lib/pwsh/util.rb
CHANGED
@@ -7,12 +7,12 @@ module Pwsh
|
|
7
7
|
module_function
|
8
8
|
|
9
9
|
# Verifies whether or not the current context is running on a Windows node.
|
10
|
+
# Implementation copied from `facets`: https://github.com/rubyworks/facets/blob/main/lib/standard/facets/rbconfig.rb
|
10
11
|
#
|
11
12
|
# @return [Bool] true if on windows
|
12
13
|
def on_windows?
|
13
|
-
|
14
|
-
|
15
|
-
!!File::ALT_SEPARATOR
|
14
|
+
host_os = RbConfig::CONFIG['host_os']
|
15
|
+
!!(host_os =~ /mswin|mingw/)
|
16
16
|
end
|
17
17
|
|
18
18
|
# Verify paths specified are valid directories which exist.
|
data/lib/pwsh/version.rb
CHANGED
data/lib/pwsh.rb
CHANGED
@@ -380,7 +380,7 @@ module Pwsh
|
|
380
380
|
pwsh_paths << File.join(path, 'pwsh.exe') if File.exist?(File.join(path, 'pwsh.exe'))
|
381
381
|
end
|
382
382
|
else
|
383
|
-
search_paths.split(
|
383
|
+
search_paths.split(':').each do |path|
|
384
384
|
pwsh_paths << File.join(path, 'pwsh') if File.exist?(File.join(path, 'pwsh'))
|
385
385
|
end
|
386
386
|
end
|
@@ -541,7 +541,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
541
541
|
|
542
542
|
context 'when the invocation script returns data without errors' do
|
543
543
|
before do
|
544
|
-
allow(ps_manager).to receive(:execute).with(script).and_return({ stdout: 'DSC Data' })
|
544
|
+
allow(ps_manager).to receive(:execute).with(script, nil).and_return({ stdout: 'DSC Data' })
|
545
545
|
allow(JSON).to receive(:parse).with('DSC Data').and_return(parsed_invocation_data)
|
546
546
|
allow(Puppet::Pops::Time::Timestamp).to receive(:parse).with('2100-01-01').and_return('TimeStamp:2100-01-01')
|
547
547
|
allow(provider).to receive(:fetch_cached_hashes).and_return([])
|
@@ -659,15 +659,15 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
659
659
|
context 'when the DSC invocation errors' do
|
660
660
|
it 'writes an error and returns nil' do
|
661
661
|
expect(provider).not_to receive(:logon_failed_already?)
|
662
|
-
expect(ps_manager).to receive(:execute).with(script).and_return({ stdout: nil })
|
663
|
-
expect(context).to receive(:err).with('Nothing returned')
|
662
|
+
expect(ps_manager).to receive(:execute).with(script, nil).and_return({ stdout: nil })
|
663
|
+
expect(context).to receive(:err).with('Nothing returned.')
|
664
664
|
expect(result).to be_nil
|
665
665
|
end
|
666
666
|
end
|
667
667
|
|
668
668
|
context 'when handling DateTimes' do
|
669
669
|
before do
|
670
|
-
allow(ps_manager).to receive(:execute).with(script).and_return({ stdout: 'DSC Data' })
|
670
|
+
allow(ps_manager).to receive(:execute).with(script, nil).and_return({ stdout: 'DSC Data' })
|
671
671
|
allow(JSON).to receive(:parse).with('DSC Data').and_return(parsed_invocation_data)
|
672
672
|
allow(provider).to receive(:fetch_cached_hashes).and_return([])
|
673
673
|
end
|
@@ -719,7 +719,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
719
719
|
context 'when the credential is invalid' do
|
720
720
|
before do
|
721
721
|
allow(provider).to receive(:logon_failed_already?).and_return(false)
|
722
|
-
allow(ps_manager).to receive(:execute).with(script).and_return({ stdout: 'DSC Data' })
|
722
|
+
allow(ps_manager).to receive(:execute).with(script, nil).and_return({ stdout: 'DSC Data' })
|
723
723
|
allow(JSON).to receive(:parse).with('DSC Data').and_return({ 'errormessage' => dsc_logon_failure_error })
|
724
724
|
allow(context).to receive(:err).with(name_hash[:name], puppet_logon_failure_error)
|
725
725
|
end
|
@@ -783,7 +783,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
783
783
|
context 'when the invocation script returns nil' do
|
784
784
|
it 'errors via context but does not raise' do
|
785
785
|
expect(ps_manager).to receive(:execute).and_return({ stdout: nil })
|
786
|
-
expect(context).to receive(:err).with('Nothing returned')
|
786
|
+
expect(context).to receive(:err).with('Nothing returned.')
|
787
787
|
expect { result }.not_to raise_error
|
788
788
|
end
|
789
789
|
end
|
@@ -835,9 +835,29 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
835
835
|
end
|
836
836
|
end
|
837
837
|
|
838
|
+
context 'when a dsc_timeout is specified' do
|
839
|
+
let(:should_hash) { name.merge(dsc_timeout: 5) }
|
840
|
+
let(:apply_props_with_timeout) { { dsc_name: 'foo', dsc_timeout: 5 } }
|
841
|
+
let(:resource_with_timeout) { "Resource: #{apply_props_with_timeout}" }
|
842
|
+
let(:script_with_timeout) { "Script: #{apply_props_with_timeout}" }
|
843
|
+
|
844
|
+
before do
|
845
|
+
allow(provider).to receive(:invocable_resource).with(apply_props_with_timeout, context, 'set').and_return(resource_with_timeout)
|
846
|
+
allow(provider).to receive(:ps_script_content).with(resource_with_timeout).and_return(script_with_timeout)
|
847
|
+
allow(provider).to receive(:remove_secret_identifiers).with(script_with_timeout).and_return(script_with_timeout)
|
848
|
+
end
|
849
|
+
|
850
|
+
it 'sets @timeout and passes it to ps_manager.execute' do
|
851
|
+
provider.instance_variable_set(:@timeout, nil)
|
852
|
+
expect(ps_manager).to receive(:execute).with(script_with_timeout, 5000).and_return({ stdout: '{"in_desired_state": true, "errormessage": null}' })
|
853
|
+
provider.invoke_set_method(context, name, should_hash)
|
854
|
+
expect(provider.instance_variable_get(:@timeout)).to eq(5000)
|
855
|
+
end
|
856
|
+
end
|
857
|
+
|
838
858
|
context 'when the invocation script returns data without errors' do
|
839
859
|
it 'filters for the correct properties to invoke and returns the results' do
|
840
|
-
expect(ps_manager).to receive(:execute).with("Script: #{apply_props}").and_return({ stdout: '{"in_desired_state": true, "errormessage": null}' })
|
860
|
+
expect(ps_manager).to receive(:execute).with("Script: #{apply_props}", nil).and_return({ stdout: '{"in_desired_state": true, "errormessage": null}' })
|
841
861
|
expect(context).not_to receive(:err)
|
842
862
|
expect(result).to eq({ 'in_desired_state' => true, 'errormessage' => nil })
|
843
863
|
end
|
@@ -2110,21 +2130,44 @@ RSpec.describe Puppet::Provider::DscBaseProvider do
|
|
2110
2130
|
end
|
2111
2131
|
|
2112
2132
|
describe '.ps_manager' do
|
2113
|
-
|
2114
|
-
|
2115
|
-
|
2116
|
-
|
2133
|
+
describe '.ps_manager on non-Windows' do
|
2134
|
+
before do
|
2135
|
+
allow(Pwsh::Util).to receive(:on_windows?).and_return(false)
|
2136
|
+
allow(Pwsh::Manager).to receive(:pwsh_path).and_return('pwsh')
|
2137
|
+
allow(Pwsh::Manager).to receive(:pwsh_args).and_return('args')
|
2138
|
+
end
|
2139
|
+
|
2140
|
+
it 'Initializes an instance of the Pwsh::Manager' do
|
2141
|
+
expect(Puppet::Util::Log).to receive(:level).and_return(:normal)
|
2142
|
+
expect(Pwsh::Manager).to receive(:instance).with('pwsh', 'args', debug: false)
|
2143
|
+
expect { provider.ps_manager }.not_to raise_error
|
2144
|
+
end
|
2117
2145
|
|
2118
|
-
|
2119
|
-
|
2120
|
-
|
2121
|
-
|
2146
|
+
it 'passes debug as true if Puppet::Util::Log.level is debug' do
|
2147
|
+
expect(Puppet::Util::Log).to receive(:level).and_return(:debug)
|
2148
|
+
expect(Pwsh::Manager).to receive(:instance).with('pwsh', 'args', debug: true)
|
2149
|
+
expect { provider.ps_manager }.not_to raise_error
|
2150
|
+
end
|
2122
2151
|
end
|
2123
2152
|
|
2124
|
-
|
2125
|
-
|
2126
|
-
|
2127
|
-
|
2153
|
+
describe '.ps_manager on Windows' do
|
2154
|
+
before do
|
2155
|
+
allow(Pwsh::Util).to receive(:on_windows?).and_return(true)
|
2156
|
+
allow(Pwsh::Manager).to receive(:powershell_path).and_return('pwsh')
|
2157
|
+
allow(Pwsh::Manager).to receive(:powershell_args).and_return('args')
|
2158
|
+
end
|
2159
|
+
|
2160
|
+
it 'Initializes an instance of the Pwsh::Manager' do
|
2161
|
+
expect(Puppet::Util::Log).to receive(:level).and_return(:normal)
|
2162
|
+
expect(Pwsh::Manager).to receive(:instance).with('pwsh', 'args', debug: false)
|
2163
|
+
expect { provider.ps_manager }.not_to raise_error
|
2164
|
+
end
|
2165
|
+
|
2166
|
+
it 'passes debug as true if Puppet::Util::Log.level is debug' do
|
2167
|
+
expect(Puppet::Util::Log).to receive(:level).and_return(:debug)
|
2168
|
+
expect(Pwsh::Manager).to receive(:instance).with('pwsh', 'args', debug: true)
|
2169
|
+
expect { provider.ps_manager }.not_to raise_error
|
2170
|
+
end
|
2128
2171
|
end
|
2129
2172
|
end
|
2130
2173
|
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.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: PowerShell code manager for ruby.
|
14
14
|
email:
|