Skip to content

Commit e887908

Browse files
committed
adjustment
1 parent 7e39c21 commit e887908

File tree

2 files changed

+79
-21
lines changed

2 files changed

+79
-21
lines changed

lib/puppet/provider/base_dsc_lite/powershell.rb

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require 'json'
55
require_relative '../../../puppet_x/puppetlabs/dsc_lite/powershell_hash_formatter'
66
require 'puppet/pops/evaluator/deferred_resolver'
7+
require 'puppet/node/facts'
78

89
Puppet::Type.type(:base_dsc_lite).provide(:powershell) do
910
confine feature: :pwshlib
@@ -40,6 +41,26 @@ def self.resolve_deferred(value)
4041
Puppet::Pops::Evaluator::DeferredResolver.resolve(value, compiler)
4142
end
4243

44+
# Add these helpers near the top of the provider (same file), outside methods:
45+
def deep_contains_deferred?(obj)
46+
case obj
47+
when Puppet::Pops::Evaluator::DeferredValue
48+
true
49+
when Hash
50+
obj.any? { |k, v| deep_contains_deferred?(k) || deep_contains_deferred?(v) }
51+
when Array
52+
obj.any? { |v| deep_contains_deferred?(v) }
53+
when Puppet::Pops::Types::PSensitiveType::Sensitive
54+
begin
55+
deep_contains_deferred?(obj.unwrap)
56+
rescue
57+
false
58+
end
59+
else
60+
false
61+
end
62+
end
63+
4364
def self.upgrade_message
4465
Puppet.warning DSC_LITE_MODULE_PUPPET_UPGRADE_MSG unless @upgrade_warning_issued
4566
@upgrade_warning_issued = true
@@ -174,6 +195,58 @@ def ps_script_content(mode)
174195
def self.ps_script_content(mode, resource, provider)
175196
dsc_invoke_method = mode
176197
@param_hash = resource
198+
199+
# Resolve any remaining Deferred values (especially nested inside :properties)
200+
begin
201+
props_param = resource.parameters[:properties]
202+
if props_param
203+
props_val = props_param.value
204+
205+
if deep_contains_deferred?(props_val)
206+
# Prefer compiler-based resolution when available
207+
compiler = Puppet.lookup(:compiler) { nil }
208+
209+
if compiler
210+
resolved = Puppet::Pops::Evaluator::DeferredResolver.resolve(props_val, compiler)
211+
# Re-assign resolved properties back to the resource
212+
resource[:properties] = resolved
213+
else
214+
# Fallback: resolve across the whole catalog in place
215+
# Try to get facts via indirection when possible; otherwise synthesize from Facter.
216+
facts = nil
217+
if resource.catalog.respond_to?(:host) && resource.catalog.host
218+
# May return nil if facts are not stored; that’s fine, we’ll synthesize.
219+
facts = begin
220+
Puppet::Node::Facts.indirection.find(resource.catalog.host)
221+
rescue
222+
nil
223+
end
224+
end
225+
facts ||= Puppet::Node::Facts.new(
226+
Facter.value(:fqdn) || Facter.value(:hostname) || 'localhost',
227+
Facter.to_hash,
228+
)
229+
230+
# Determine environment instance when available
231+
env = if resource.catalog.respond_to?(:environment_instance)
232+
resource.catalog.environment_instance
233+
else
234+
Puppet.lookup(:current_environment) { nil }
235+
end
236+
237+
# Resolve all Deferreds in the catalog (safe, official API)
238+
Puppet::Pops::Evaluator::DeferredResolver.resolve_and_replace(facts, resource.catalog, env, true)
239+
240+
# Refresh our local value after resolution (in case it changed)
241+
props_param = resource.parameters[:properties]
242+
resource[:properties] = props_param.value if props_param
243+
end
244+
end
245+
end
246+
rescue => e
247+
raise Puppet::Error, "DSC_lite: failed to resolve Deferred values in `properties`: #{e.class}: #{e.message}"
248+
end
249+
177250
template_name = resource.generic_dsc ? '/invoke_generic_dsc_resource.ps1.erb' : '/invoke_dsc_resource.ps1.erb'
178251
file = File.new(template_path + template_name, encoding: Encoding::UTF_8)
179252
template = ERB.new(file.read, trim_mode: '-')

spec/acceptance/deferred_spec.rb

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,13 @@ def read_fixture(name)
88
end
99

1010
def read_win_file_if_exists(path)
11-
# Use a script block with literals; avoid $variables to prevent transport/quoting expansion
12-
# Also keep exit 0 regardless of existence so run_shell doesn't raise.
1311
ps = %{& { if (Test-Path -LiteralPath '#{path}') { Get-Content -Raw -LiteralPath '#{path}' } else { '<<<FILE_NOT_FOUND>>>' } } }
1412
r = run_shell(%(powershell.exe -NoProfile -NonInteractive -Command "#{ps}"))
1513
body = (r.stdout || '').to_s
1614
exists = !body.include?('<<<FILE_NOT_FOUND>>>')
1715
{ exists: exists, content: exists ? body : '' }
1816
end
1917

20-
# ---- NEW: wrappers that always pass the required flags ----
2118
def puppet_flags
2219
[
2320
'--preprocess_deferred', 'true',
@@ -34,22 +31,16 @@ def apply_manifest_with_flags(manifest, opts = {})
3431
def idempotent_apply_with_flags(manifest, opts = {})
3532
idempotent_apply(manifest, opts.merge(puppet_options: puppet_flags))
3633
end
37-
# ----------------------------------------------------------
38-
3934
RSpec.configure do |config|
4035
config.before(:all) do
41-
# Ensure Deferred values are preprocessed before provider runs (Puppet 8 default is false)
4236
run_shell('puppet config set preprocess_deferred true --section agent')
43-
# Ensure JSON catalog serialization (avoids PSON issues with Deferred)
4437
run_shell('puppet config set preferred_serialization_format json --section agent')
4538

46-
# Assert settings for clear diagnostics in CI logs
4739
pp = run_shell('puppet config print preprocess_deferred --section agent').stdout.strip
4840
fmt = run_shell('puppet config print preferred_serialization_format --section agent').stdout.strip
4941
raise 'preprocess_deferred not true on agent!' unless pp.casecmp('true').zero?
5042
raise 'preferred_serialization_format not json on agent!' unless fmt.casecmp('json').zero?
5143

52-
# Extra visibility: show puppet version & confdir in logs
5344
run_shell('puppet --version')
5445
run_shell('puppet config print confdir')
5546
end
@@ -58,21 +49,16 @@ def idempotent_apply_with_flags(manifest, opts = {})
5849
describe 'deferred values with dsc_lite' do
5950
let(:control_manifest) { read_fixture('01_file_deferred.pp') }
6051
let(:dsc_deferred_direct) { read_fixture('02_dsc_deferred_direct.pp') }
61-
let(:dsc_deferred_inline) { read_fixture('02b_dsc_deferred_inline.pp') } # NEW
52+
let(:dsc_deferred_inline) { read_fixture('02b_dsc_deferred_inline.pp') }
6253
let(:dsc_deferred_stringified) { read_fixture('03a_dsc_deferred_stringified.pp') }
6354
let(:dsc_deferred_bad_unwrap) { read_fixture('03b_dsc_deferred_bad_unwrap.pp') }
6455

65-
# ---- NEW: diagnostic example to prove effective settings for this process ----
66-
it '00: confirms preprocess_deferred=true and JSON are active in the current apply context' do
67-
print_pp = <<-PP
68-
notify { "preprocess_deferred=#{Puppet.lookup(:settings).value('preprocess_deferred')}": }
69-
notify { "preferred_serialization_format=#{Puppet.lookup(:settings).value('preferred_serialization_format')}": }
70-
PP
71-
res = apply_manifest_with_flags(print_pp)
72-
expect(res.stdout).to match(%r{preprocess_deferred=true}i)
73-
expect(res.stdout).to match(%r{preferred_serialization_format=json}i)
56+
it '00: confirms preprocess_deferred=true and JSON are active in this context' do
57+
pp_out = run_shell('puppet config print preprocess_deferred --section agent').stdout.strip
58+
fmt_out = run_shell('puppet config print preferred_serialization_format --section agent').stdout.strip
59+
expect(pp_out.downcase).to eq('true')
60+
expect(fmt_out.downcase).to eq('json')
7461
end
75-
# ------------------------------------------------------------------------------
7662

7763
it 'control (01): native file + Deferred resolves to hello-file' do
7864
result = idempotent_apply_with_flags(control_manifest)
@@ -95,7 +81,6 @@ def idempotent_apply_with_flags(manifest, opts = {})
9581
end
9682
end
9783

98-
# NEW 02b: inline Deferred on the DSC property (no variable intermediary)
9984
it '02b: passing Deferred inline to DSC resolves to hello-dsc-inline (otherwise flag bug)' do
10085
apply = apply_manifest_with_flags(dsc_deferred_inline)
10186
out = read_win_file_if_exists('C:/Temp/from_dsc_inline.txt')

0 commit comments

Comments
 (0)