|
3 | 3 | require 'pathname' |
4 | 4 | require 'json' |
5 | 5 | require_relative '../../../puppet_x/puppetlabs/dsc_lite/powershell_hash_formatter' |
| 6 | +require 'puppet/pops/evaluator/deferred_resolver' |
| 7 | +require 'puppet/node/facts' |
6 | 8 |
|
7 | 9 | Puppet::Type.type(:base_dsc_lite).provide(:powershell) do |
8 | 10 | confine feature: :pwshlib |
@@ -65,11 +67,68 @@ def ps_manager |
65 | 67 | Pwsh::Manager.instance(command(:powershell), Pwsh::Manager.powershell_args, debug: debug_output) |
66 | 68 | end |
67 | 69 |
|
| 70 | + # Returns true if obj (or any nested value) is a DeferredValue |
| 71 | + def deep_contains_deferred?(obj) |
| 72 | + case obj |
| 73 | + when Puppet::Pops::Evaluator::DeferredValue |
| 74 | + true |
| 75 | + when Hash |
| 76 | + obj.any? { |k, v| deep_contains_deferred?(k) || deep_contains_deferred?(v) } |
| 77 | + when Array |
| 78 | + obj.any? { |v| deep_contains_deferred?(v) } |
| 79 | + when Puppet::Pops::Types::PSensitiveType::Sensitive |
| 80 | + begin |
| 81 | + deep_contains_deferred?(obj.unwrap) |
| 82 | + rescue |
| 83 | + false |
| 84 | + end |
| 85 | + else |
| 86 | + false |
| 87 | + end |
| 88 | + end |
| 89 | + |
| 90 | + # Resolve any lingering Deferred values in the `properties` parameter |
| 91 | + def resolve_properties_deferreds! |
| 92 | + props_param = resource.parameters[:properties] |
| 93 | + return unless props_param |
| 94 | + |
| 95 | + props_val = props_param.value |
| 96 | + return unless deep_contains_deferred?(props_val) |
| 97 | + |
| 98 | + # Prefer compiler-based resolution when available |
| 99 | + compiler = Puppet.lookup(:compiler) { nil } |
| 100 | + if compiler |
| 101 | + resolved = Puppet::Pops::Evaluator::DeferredResolver.resolve(props_val, compiler) |
| 102 | + resource[:properties] = resolved |
| 103 | + return |
| 104 | + end |
| 105 | + |
| 106 | + # Fallback: resolve across the whole catalog in place (official API) |
| 107 | + facts = Puppet::Node::Facts.new( |
| 108 | + (resource.catalog.respond_to?(:host) && resource.catalog.host) || |
| 109 | + Facter.value(:fqdn) || Facter.value(:hostname) || 'localhost', |
| 110 | + Facter.to_hash, |
| 111 | + ) |
| 112 | + env = resource.catalog.respond_to?(:environment_instance) ? resource.catalog.environment_instance : nil |
| 113 | + |
| 114 | + Puppet::Pops::Evaluator::DeferredResolver.resolve_and_replace(facts, resource.catalog, env, true) |
| 115 | + |
| 116 | + # Refresh our local value after in-place resolution |
| 117 | + updated = resource.parameters[:properties] |
| 118 | + resource[:properties] = updated.value if updated |
| 119 | + rescue => e |
| 120 | + raise Puppet::Error, "DSC_lite: failed to resolve Deferred values in `properties`: #{e.class}: #{e.message}" |
| 121 | + end |
| 122 | + |
68 | 123 | def exists? |
69 | 124 | timeout = set_timeout |
70 | 125 | Puppet.debug "Dsc Timeout: #{timeout} milliseconds" |
71 | 126 | version = Facter.value(:powershell_version) |
72 | 127 | Puppet.debug "PowerShell Version: #{version}" |
| 128 | + |
| 129 | + # NEW: resolve any lingering DeferredValue in properties |
| 130 | + resolve_properties_deferreds! |
| 131 | + |
73 | 132 | script_content = ps_script_content('test') |
74 | 133 | Puppet.debug "\n" + self.class.redact_content(script_content) |
75 | 134 |
|
@@ -97,6 +156,10 @@ def exists? |
97 | 156 | def create |
98 | 157 | timeout = set_timeout |
99 | 158 | Puppet.debug "Dsc Timeout: #{timeout} milliseconds" |
| 159 | + |
| 160 | + # NEW: resolve any lingering DeferredValue in properties |
| 161 | + resolve_properties_deferreds! |
| 162 | + |
100 | 163 | script_content = ps_script_content('set') |
101 | 164 | Puppet.debug "\n" + self.class.redact_content(script_content) |
102 | 165 |
|
|
0 commit comments