Skip to content

Commit bb1017f

Browse files
authored
Merge pull request #8802 from GabrielNagy/PUP-11328/node-request-fallback
(PUP-11328) Fallback to node request in configurer
2 parents aaae117 + 867691b commit bb1017f

File tree

3 files changed

+182
-33
lines changed

3 files changed

+182
-33
lines changed

lib/puppet/configurer.rb

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -302,9 +302,16 @@ def run_internal(options)
302302
# We only need to find out the environment to run in if we don't already have a catalog
303303
unless (cached_catalog || options[:catalog] || Puppet.settings.set_by_cli?(:environment) || Puppet[:strict_environment_mode])
304304
Puppet.debug(_("Environment not passed via CLI and no catalog was given, attempting to find out the last server-specified environment"))
305-
if last_server_specified_environment
306-
@environment = last_server_specified_environment
307-
report.environment = last_server_specified_environment
305+
initial_environment, loaded_last_environment = last_server_specified_environment
306+
307+
unless loaded_last_environment
308+
Puppet.debug(_("Requesting environment from the server"))
309+
initial_environment = current_server_specified_environment(@environment, configured_environment, options)
310+
end
311+
312+
if initial_environment
313+
@environment = initial_environment
314+
report.environment = initial_environment
308315

309316
push_current_environment_and_loaders
310317
else
@@ -463,24 +470,77 @@ def find_functional_server
463470
end
464471
private :find_functional_server
465472

473+
#
474+
# @api private
475+
#
476+
# Read the last server-specified environment from the lastrunfile. The
477+
# environment is considered to be server-specified if the values of
478+
# `initial_environment` and `converged_environment` are different.
479+
#
480+
# @return [String, Boolean] An array containing a string with the environment
481+
# read from the lastrunfile in case the server is authoritative, and a
482+
# boolean marking whether the last environment was correctly loaded.
466483
def last_server_specified_environment
467-
return @last_server_specified_environment if @last_server_specified_environment
484+
return @last_server_specified_environment, @loaded_last_environment if @last_server_specified_environment
485+
468486
if Puppet::FileSystem.exist?(Puppet[:lastrunfile])
469487
summary = Puppet::Util::Yaml.safe_load_file(Puppet[:lastrunfile])
470-
return unless summary.dig('application', 'run_mode') == 'agent'
471-
initial_environment = summary.dig('application', 'initial_environment')
472-
converged_environment = summary.dig('application', 'converged_environment')
488+
return [nil, nil] unless summary['application']['run_mode'] == 'agent'
489+
initial_environment = summary['application']['initial_environment']
490+
converged_environment = summary['application']['converged_environment']
473491
@last_server_specified_environment = converged_environment if initial_environment != converged_environment
492+
Puppet.debug(_("Successfully loaded last environment from the lastrunfile"))
493+
@loaded_last_environment = true
474494
end
475495

476496
Puppet.debug(_("Found last server-specified environment: %{environment}") % { environment: @last_server_specified_environment }) if @last_server_specified_environment
477-
@last_server_specified_environment
497+
[@last_server_specified_environment, @loaded_last_environment]
478498
rescue => detail
479499
Puppet.debug(_("Could not find last server-specified environment: %{detail}") % { detail: detail })
480-
nil
500+
[nil, nil]
481501
end
482502
private :last_server_specified_environment
483503

504+
def current_server_specified_environment(current_environment, configured_environment, options)
505+
return @server_specified_environment if @server_specified_environment
506+
507+
begin
508+
node_retr_time = thinmark do
509+
node = Puppet::Node.indirection.find(Puppet[:node_name_value],
510+
:environment => Puppet::Node::Environment.remote(current_environment),
511+
:configured_environment => configured_environment,
512+
:ignore_cache => true,
513+
:transaction_uuid => @transaction_uuid,
514+
:fail_on_404 => true)
515+
516+
# The :rest node terminus returns a node with an environment_name, but not an
517+
# environment instance. Attempting to get the environment instance will load
518+
# it from disk, which will likely fail. So create a remote environment.
519+
#
520+
# The :plain node terminus returns a node with an environment, but not an
521+
# environment_name.
522+
if !node.has_environment_instance? && node.environment_name
523+
node.environment = Puppet::Node::Environment.remote(node.environment_name)
524+
end
525+
526+
@server_specified_environment = node.environment.to_s
527+
528+
if @server_specified_environment != @environment
529+
Puppet.notice _("Local environment: '%{local_env}' doesn't match server specified node environment '%{node_env}', switching agent to '%{node_env}'.") % { local_env: @environment, node_env: @server_specified_environment }
530+
end
531+
end
532+
533+
options[:report].add_times(:node_retrieval, node_retr_time)
534+
535+
@server_specified_environment
536+
rescue => detail
537+
Puppet.warning(_("Unable to fetch my node definition, but the agent run will continue:"))
538+
Puppet.warning(detail)
539+
nil
540+
end
541+
end
542+
private :current_server_specified_environment
543+
484544
def send_report(report)
485545
puts report.summary if Puppet[:summarize]
486546
save_last_run_summary(report)

spec/integration/application/agent_spec.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,34 @@ def with_another_agent_running(&block)
642642
end
643643

644644
context "environment convergence" do
645+
it "falls back to making a node request if the last server-specified environment cannot be loaded" do
646+
mounts = {}
647+
mounts[:node] = -> (req, res) {
648+
node = Puppet::Node.new('test', environment: Puppet::Node::Environment.remote('doesnotexistonagent'))
649+
res.body = formatter.render(node)
650+
res['Content-Type'] = formatter.mime
651+
}
652+
653+
server.start_server(mounts: mounts) do |port|
654+
Puppet[:serverport] = port
655+
Puppet[:log_level] = 'debug'
656+
657+
expect {
658+
agent.command_line.args << '--test'
659+
agent.run
660+
}.to exit_with(0)
661+
.and output(a_string_matching(%r{Debug: Requesting environment from the server})).to_stdout
662+
663+
Puppet::Application.clear!
664+
665+
expect {
666+
agent.command_line.args << '--test'
667+
agent.run
668+
}.to exit_with(0)
669+
.and output(a_string_matching(%r{Debug: Successfully loaded last environment from the lastrunfile})).to_stdout
670+
end
671+
end
672+
645673
it "switches to 'newenv' environment and retries the run" do
646674
first_run = true
647675
libdir = File.join(my_fixture_dir, 'lib')

spec/unit/configurer_spec.rb

Lines changed: 85 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
require 'puppet/configurer'
33

44
describe Puppet::Configurer do
5+
include PuppetSpec::Files
6+
57
before do
68
Puppet[:server] = "puppetmaster"
79
Puppet[:report] = true
@@ -10,6 +12,17 @@
1012
allow_any_instance_of(described_class).to(
1113
receive(:valid_server_environment?).and_return(true)
1214
)
15+
16+
Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)
17+
---
18+
version:
19+
config: 1624882680
20+
puppet: #{Puppet.version}
21+
application:
22+
initial_environment: #{Puppet[:environment]}
23+
converged_environment: #{Puppet[:environment]}
24+
run_mode: agent
25+
SUMMARY
1326
end
1427

1528
let(:node_name) { Puppet[:node_name_value] }
@@ -1099,7 +1112,6 @@ def expects_neither_new_or_cached_catalog
10991112
end
11001113

11011114
describe "when selecting an environment" do
1102-
include PuppetSpec::Files
11031115
include PuppetSpec::Settings
11041116

11051117
describe "when the last used environment is available" do
@@ -1116,6 +1128,9 @@ def expects_neither_new_or_cached_catalog
11161128
converged_environment: #{last_server_specified_environment}
11171129
run_mode: agent
11181130
SUMMARY
1131+
1132+
expect(Puppet::Node.indirection).not_to receive(:find)
1133+
.with(anything, hash_including(:ignore_cache => true, :fail_on_404 => true))
11191134
end
11201135

11211136
it "prefers the environment set via cli" do
@@ -1125,26 +1140,27 @@ def expects_neither_new_or_cached_catalog
11251140
expect(configurer.environment).to eq('usethis')
11261141
end
11271142

1128-
it "prefers the environment set via config" do
1143+
it "prefers the environment set via lastrunfile over config" do
11291144
FileUtils.mkdir_p(Puppet[:confdir])
11301145
set_puppet_conf(Puppet[:confdir], <<~CONF)
11311146
[main]
11321147
environment = usethis
1148+
lastrunfile = #{Puppet[:lastrunfile]}
11331149
CONF
11341150

11351151
Puppet.initialize_settings
11361152
configurer.run
11371153

1138-
expect(configurer.environment).to eq('usethis')
1154+
expect(configurer.environment).to eq(last_server_specified_environment)
11391155
end
11401156

1141-
it "uses environment from Puppet[:environment] if given a catalog" do
1157+
it "uses the environment from Puppet[:environment] if given a catalog" do
11421158
configurer.run(catalog: catalog)
11431159

11441160
expect(configurer.environment).to eq(Puppet[:environment])
11451161
end
11461162

1147-
it "uses environment from Puppet[:environment] if use_cached_catalog = true" do
1163+
it "uses the environment from Puppet[:environment] if use_cached_catalog = true" do
11481164
Puppet[:use_cached_catalog] = true
11491165
expects_cached_catalog_only(catalog)
11501166
configurer.run
@@ -1173,14 +1189,14 @@ def expects_neither_new_or_cached_catalog
11731189
configurer.run
11741190
end
11751191

1176-
it "uses environment from Puppet[:environment] if strict_environment_mode is set" do
1192+
it "uses the environment from Puppet[:environment] if strict_environment_mode is set" do
11771193
Puppet[:strict_environment_mode] = true
11781194
configurer.run
11791195

11801196
expect(configurer.environment).to eq(Puppet[:environment])
11811197
end
11821198

1183-
it "uses environment from Puppet[:environment] if initial_environment is the same as converged_environment" do
1199+
it "uses the environment from Puppet[:environment] if initial_environment is the same as converged_environment" do
11841200
Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)
11851201
---
11861202
version:
@@ -1195,41 +1211,86 @@ def expects_neither_new_or_cached_catalog
11951211

11961212
expect(configurer.environment).to eq(Puppet[:environment])
11971213
end
1214+
end
1215+
end
1216+
1217+
describe "when the last used environment is not available" do
1218+
describe "when the node request succeeds" do
1219+
let(:node_environment) { Puppet::Node::Environment.remote(:salam) }
1220+
let(:node) { Puppet::Node.new(Puppet[:node_name_value]) }
1221+
let(:last_server_specified_environment) { 'development' }
1222+
1223+
before do
1224+
node.environment = node_environment
1225+
1226+
allow(Puppet::Node.indirection).to receive(:find)
1227+
allow(Puppet::Node.indirection).to receive(:find)
1228+
.with(anything, hash_including(:ignore_cache => true, :fail_on_404 => true))
1229+
.and_return(node)
1230+
end
11981231

1199-
it "uses environment from Puppet[:environment] if the run mode doesn't match" do
1232+
it "uses the environment from the node request if the run mode doesn't match" do
12001233
Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)
1201-
---
1202-
version:
1203-
config: 1624882680
1204-
puppet: 6.24.0
1205-
application:
1206-
initial_environment: #{Puppet[:environment]}
1207-
converged_environment: #{last_server_specified_environment}
1208-
run_mode: user
1234+
---
1235+
version:
1236+
config: 1624882680
1237+
puppet: 6.24.0
1238+
application:
1239+
initial_environment: #{Puppet[:environment]}
1240+
converged_environment: #{last_server_specified_environment}
1241+
run_mode: user
12091242
SUMMARY
12101243
configurer.run
12111244

1212-
expect(configurer.environment).to eq(Puppet[:environment])
1245+
expect(configurer.environment).to eq(node_environment.name.to_s)
12131246
end
12141247

1215-
it "uses environment from Puppet[:environment] if lastrunfile is invalid YAML" do
1248+
it "uses the environment from the node request if lastrunfile does not contain the expected keys" do
12161249
Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)
1217-
Key: 'this is my very very very ' +
1218-
'long string'
1250+
---
1251+
version:
1252+
config: 1624882680
1253+
puppet: 6.24.0
12191254
SUMMARY
12201255
configurer.run
12211256

1222-
expect(configurer.environment).to eq(Puppet[:environment])
1257+
expect(configurer.environment).to eq(node_environment.name.to_s)
12231258
end
12241259

1225-
it "uses environment from Puppet[:environment] if lastrunfile exists but is empty" do
1260+
it "uses the environment from the node request if lastrunfile is invalid YAML" do
1261+
Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)
1262+
Key: 'this is my very very very ' +
1263+
'long string'
1264+
SUMMARY
1265+
configurer.run
1266+
1267+
expect(configurer.environment).to eq(node_environment.name.to_s)
1268+
end
1269+
1270+
it "uses the environment from the node request if lastrunfile exists but is empty" do
12261271
Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', '')
12271272
configurer.run
12281273

1229-
expect(configurer.environment).to eq(Puppet[:environment])
1274+
expect(configurer.environment).to eq(node_environment.name.to_s)
1275+
end
1276+
1277+
it "uses the environment from the node request if the last used one cannot be found" do
1278+
Puppet[:lastrunfile] = tmpfile('last_run_summary.yaml')
1279+
configurer.run
1280+
1281+
expect(configurer.environment).to eq(node_environment.name.to_s)
1282+
end
1283+
end
1284+
1285+
describe "when the node request fails" do
1286+
before do
1287+
allow(Puppet::Node.indirection).to receive(:find).and_call_original
1288+
allow(Puppet::Node.indirection).to receive(:find)
1289+
.with(anything, hash_including(:ignore_cache => true, :fail_on_404 => true))
1290+
.and_raise(Puppet::Error)
12301291
end
12311292

1232-
it "uses environment from Puppet[:environment] if the last used one cannot be found" do
1293+
it "uses the environment from Puppet[:environment] if the last used one cannot be found" do
12331294
Puppet[:lastrunfile] = tmpfile('last_run_summary.yaml')
12341295
configurer.run
12351296

0 commit comments

Comments
 (0)