Skip to content

Commit 4ad4b4d

Browse files
committed
cleanup
1 parent 8b4db04 commit 4ad4b4d

File tree

2 files changed

+30
-165
lines changed

2 files changed

+30
-165
lines changed

lib/puppet/provider/base_dsc_lite/powershell.rb

Lines changed: 30 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,6 @@
22

33
require 'pathname'
44
require 'json'
5-
require 'erb' # ensure ERB is available
6-
require 'puppet'
7-
require 'puppet/node'
8-
require 'puppet/parser/script_compiler'
9-
require 'puppet/pops/evaluator/deferred_resolver'
105
require_relative '../../../puppet_x/puppetlabs/dsc_lite/powershell_hash_formatter'
116

127
Puppet::Type.type(:base_dsc_lite).provide(:powershell) do
@@ -36,10 +31,6 @@
3631
Puppet (including 3.x), or to a Puppet version newer than 3.x.
3732
UPGRADE
3833

39-
# ---------------------------
40-
# Class helpers
41-
# ---------------------------
42-
4334
def self.upgrade_message
4435
Puppet.warning DSC_LITE_MODULE_PUPPET_UPGRADE_MSG unless @upgrade_warning_issued
4536
@upgrade_warning_issued = true
@@ -49,53 +40,20 @@ def self.vendored_modules_path
4940
File.expand_path(Pathname.new(__FILE__).dirname + '../../../' + 'puppet_x/dsc_resources')
5041
end
5142

52-
def self.template_path
53-
File.expand_path(Pathname.new(__FILE__).dirname)
54-
end
55-
56-
def self.format_dsc_lite(dsc_value)
57-
PuppetX::PuppetLabs::DscLite::PowerShellHashFormatter.format(dsc_value)
58-
end
59-
60-
def self.escape_quotes(text)
61-
text.gsub("'", "''")
62-
end
63-
64-
def self.redact_content(content)
65-
# Redact Sensitive unwraps that appear as "'secret' # PuppetSensitive"
66-
content.gsub(%r{= '.+' # PuppetSensitive;?(\\n)?$}, "= '[REDACTED]'")
67-
end
68-
69-
# ---------------------------
70-
# Deferred value resolution - NEW APPROACH
71-
# ---------------------------
72-
7343
# Resolve deferred values in properties right before PowerShell script generation
74-
# This is the correct timing - after catalog application starts but before template rendering
7544
def resolve_deferred_values!
7645
return unless resource.parameters.key?(:properties)
7746

7847
current_properties = resource.parameters[:properties].value
7948
return unless contains_deferred_values?(current_properties)
8049

81-
Puppet.notice('DSC PROVIDER → Resolving deferred values in properties')
82-
8350
begin
8451
# Resolve deferred values directly using the properties hash
8552
resolved_properties = manually_resolve_deferred_values(current_properties)
86-
8753
# Update the resource with resolved properties
8854
resource.parameters[:properties].value = resolved_properties
89-
90-
# Verify resolution worked
91-
if contains_deferred_values?(resolved_properties)
92-
Puppet.warning('DSC PROVIDER → Some deferred values could not be resolved')
93-
else
94-
Puppet.notice('DSC PROVIDER → All deferred values resolved successfully')
95-
end
9655
rescue => e
9756
Puppet.warning("DSC PROVIDER → Error resolving deferred values: #{e.class}: #{e.message}")
98-
Puppet.debug("DSC PROVIDER → Error backtrace: #{e.backtrace.join("\n")}")
9957
# Continue with unresolved values - they will be stringified but at least won't crash
10058
end
10159
end
@@ -119,25 +77,17 @@ def manually_resolve_deferred_values(value)
11977
# DeferredValue objects have a @proc instance variable we can call
12078
proc = value.instance_variable_get(:@proc)
12179
return proc.call if proc && proc.respond_to?(:call)
122-
123-
Puppet.debug('DSC PROVIDER → DeferredValue has no callable proc')
12480
return value.to_s
125-
12681
elsif value && value.class.name.include?('Deferred')
12782
# For other Deferred types, try standard resolution
128-
if value.respond_to?(:name)
129-
begin
130-
return Puppet::Pops::Evaluator::DeferredResolver.resolve(value.name, nil, {})
131-
rescue => e
132-
Puppet.debug("DSC PROVIDER → Failed to resolve Deferred object: #{e.message}")
133-
return value.to_s
134-
end
135-
else
136-
Puppet.debug('DSC PROVIDER → Deferred object has no name method')
83+
return value.to_s unless value.respond_to?(:name)
84+
begin
85+
return Puppet::Pops::Evaluator::DeferredResolver.resolve(value.name, nil, {})
86+
rescue
13787
return value.to_s
13888
end
139-
end
14089

90+
end
14191
# Return the value unchanged if it's not deferred
14292
value
14393
end
@@ -157,92 +107,6 @@ def contains_deferred_values?(value)
157107
end
158108
end
159109

160-
# ---------------------------
161-
# Legacy deferred resolution methods (keeping for reference but not using)
162-
# ---------------------------
163-
164-
# 1) Catalog-wide resolve: replace all Deferreds/futures in the catalog
165-
def force_resolve_catalog_deferred!
166-
cat = resource&.catalog
167-
return unless cat
168-
169-
facts = Puppet.lookup(:facts) { nil }
170-
env = if cat.respond_to?(:environment_instance)
171-
cat.environment_instance
172-
else
173-
Puppet.lookup(:current_environment) { nil }
174-
end
175-
176-
begin
177-
Puppet::Pops::Evaluator::DeferredResolver.resolve_and_replace(facts, cat, env, true)
178-
Puppet.notice('DSC PROVIDER SENTINEL → resolve_and_replace invoked')
179-
rescue => e
180-
Puppet.notice("DSC PROVIDER resolve_and_replace error: #{e.class}: #{e.message}")
181-
end
182-
end
183-
184-
# Build a compiler on the agent for local resolution
185-
def build_agent_compiler(env)
186-
node_name = Puppet[:node_name_value]
187-
node = Puppet::Node.new(node_name, environment: env)
188-
189-
# Attaching facts improves function behavior during resolve (best-effort)
190-
begin
191-
facts = Puppet.lookup(:facts) { nil }
192-
node.add_facts(facts) if facts
193-
rescue => e
194-
Puppet.debug("DSC_lite: could not attach facts to node for local resolve: #{e.class}: #{e.message}")
195-
end
196-
197-
if defined?(Puppet::Parser::ScriptCompiler)
198-
Puppet::Parser::ScriptCompiler.new(node, env)
199-
else
200-
Puppet::Parser::Compiler.new(node)
201-
end
202-
end
203-
204-
# 2) Targeted resolve: explicitly resolve the :properties value using a resolver that
205-
# handles both Deferred and evaluator futures, then write it back to the resource.
206-
def force_resolve_properties!
207-
return unless resource.parameters.key?(:properties)
208-
209-
cat = resource&.catalog
210-
env = if cat&.respond_to?(:environment_instance)
211-
cat.environment_instance
212-
else
213-
Puppet.lookup(:current_environment) { nil }
214-
end
215-
216-
begin
217-
compiler = build_agent_compiler(env) if env
218-
return unless compiler # without a compiler, local resolve can't proceed
219-
220-
facts = Puppet.lookup(:facts) { nil }
221-
222-
resolver = Puppet::Pops::Evaluator::DeferredResolver.new(compiler, true)
223-
resolver.set_facts_variable(facts) if facts
224-
225-
# Read current value, resolve deeply, then write back into the parameter
226-
current = resource.parameters[:properties].value
227-
resolved = resolver.resolve(current)
228-
resource.parameters[:properties].value = resolved
229-
230-
# Post-check: what class is properties['Contents'] now?
231-
cls = if resolved.is_a?(Hash) && resolved.key?('Contents')
232-
resolved['Contents'].class
233-
else
234-
resolved.class
235-
end
236-
Puppet.notice("DSC PROVIDER SENTINEL → post-resolve properties.Contents: #{cls}")
237-
rescue => e
238-
Puppet.debug("DSC_lite: explicit properties resolve failed: #{e.class}: #{e.message}")
239-
end
240-
end
241-
242-
# ---------------------------
243-
# Existing provider helpers
244-
# ---------------------------
245-
246110
def dsc_parameters
247111
resource.parameters_with_value.select do |p|
248112
p.name.to_s.include? 'dsc_'
@@ -255,6 +119,10 @@ def dsc_property_param
255119
end
256120
end
257121

122+
def self.template_path
123+
File.expand_path(Pathname.new(__FILE__).dirname)
124+
end
125+
258126
def set_timeout
259127
resource[:dsc_timeout] ? resource[:dsc_timeout] * 1000 : 1_200_000
260128
end
@@ -264,32 +132,21 @@ def ps_manager
264132
Pwsh::Manager.instance(command(:powershell), Pwsh::Manager.powershell_args, debug: debug_output)
265133
end
266134

267-
# Keep for ERBs that call provider.format_for_ps(...)
268-
def format_for_ps(value)
269-
self.class.format_dsc_lite(value)
270-
end
271-
272-
# ---------------------------
273-
# Provider operations
274-
# ---------------------------
275-
276135
def exists?
277-
Puppet.notice("DSC PROVIDER SENTINEL → #{__FILE__}")
278-
279136
# Resolve deferred values right before we start processing
280137
resolve_deferred_values!
281138

282139
timeout = set_timeout
283140
Puppet.debug "Dsc Timeout: #{timeout} milliseconds"
284141
version = Facter.value(:powershell_version)
285142
Puppet.debug "PowerShell Version: #{version}"
286-
287143
script_content = ps_script_content('test')
288144
Puppet.debug "\n" + self.class.redact_content(script_content)
289145

290146
if Pwsh::Manager.windows_powershell_supported?
291147
output = ps_manager.execute(script_content, timeout)
292148
raise Puppet::Error, output[:errormessage] if output[:errormessage]&.match?(%r{PowerShell module timeout \(\d+ ms\) exceeded while executing})
149+
293150
output = output[:stdout]
294151
else
295152
self.class.upgrade_message
@@ -308,20 +165,18 @@ def exists?
308165
end
309166

310167
def create
311-
Puppet.notice("DSC PROVIDER SENTINEL → #{__FILE__}")
312-
313168
# Resolve deferred values right before we start processing
314169
resolve_deferred_values!
315170

316171
timeout = set_timeout
317172
Puppet.debug "Dsc Timeout: #{timeout} milliseconds"
318-
319173
script_content = ps_script_content('set')
320174
Puppet.debug "\n" + self.class.redact_content(script_content)
321175

322176
if Pwsh::Manager.windows_powershell_supported?
323177
output = ps_manager.execute(script_content, timeout)
324178
raise Puppet::Error, output[:errormessage] if output[:errormessage]&.match?(%r{PowerShell module timeout \(\d+ ms\) exceeded while executing})
179+
325180
output = output[:stdout]
326181
else
327182
self.class.upgrade_message
@@ -332,6 +187,7 @@ def create
332187
data = JSON.parse(output)
333188

334189
raise(data['errormessage']) unless data['errormessage'].empty?
190+
335191
notify_reboot_pending if data['rebootrequired'] == true
336192

337193
data
@@ -355,19 +211,32 @@ def notify_reboot_pending
355211
end
356212
end
357213

214+
def self.format_dsc_lite(dsc_value)
215+
PuppetX::PuppetLabs::DscLite::PowerShellHashFormatter.format(dsc_value)
216+
end
217+
218+
def self.escape_quotes(text)
219+
text.gsub("'", "''")
220+
end
221+
222+
def self.redact_content(content)
223+
# Note that here we match after an equals to ensure we redact the value being passed, but not the key.
224+
# This means a redaction of a string not including '= ' before the string value will not redact.
225+
# Every secret unwrapped in this module will unwrap as "'secret' # PuppetSensitive" and, currently,
226+
# always inside a hash table to be passed along. This means we can (currently) expect the value to
227+
# always come after an equals sign.
228+
# Note that the line may include a semi-colon and/or a newline character after the sensitive unwrap.
229+
content.gsub(%r{= '.+' # PuppetSensitive;?(\\n)?$}, "= '[REDACTED]'")
230+
end
231+
358232
def ps_script_content(mode)
359233
self.class.ps_script_content(mode, resource, self)
360234
end
361235

362236
def self.ps_script_content(mode, resource, provider)
363-
dsc_invoke_method = mode
364237
@param_hash = resource
365238
template_name = resource.generic_dsc ? '/invoke_generic_dsc_resource.ps1.erb' : '/invoke_dsc_resource.ps1.erb'
366239
file = File.new(template_path + template_name, encoding: Encoding::UTF_8)
367-
368-
# Make vendored_modules_path visible in ERB if the template uses it
369-
vendored_modules_path = self.vendored_modules_path
370-
371240
template = ERB.new(file.read, trim_mode: '-')
372241
template.result(binding)
373242
end

lib/puppet/type/dsc.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# frozen_string_literal: true
22

33
require 'pathname'
4-
require 'puppet/pops/evaluator/deferred_resolver' # ← add: resolver API
54

65
Puppet::Type.newtype(:dsc) do
76
desc <<-DOC
@@ -109,9 +108,6 @@ def change_to_s(currentvalue, newvalue)
109108
munge do |value|
110109
# Don't try to resolve deferred values during munge - they should be resolved
111110
# on the agent during catalog application, not during catalog compilation.
112-
# Just pass them through to the provider for proper resolution timing.
113-
Puppet.notice("DSC TYPE SENTINEL → #{__FILE__} - letting deferred values pass through")
114-
115111
PuppetX::DscLite::TypeHelpers.munge_sensitive_hash(value)
116112
end
117113
end

0 commit comments

Comments
 (0)