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