Skip to content

Commit f0214e6

Browse files
GabrielNagyjoshcooper
authored andcommitted
(PUP-11328) Fallback to node request in configurer
If we can't load our last server specified environment, then fallback to making a node request like we used to. This way if we're supposed to use a server specified environment and we upgrade, then we don't revert to "production" and redownload plugins. A node request will not be made if the `initial_environment` and the `converged_environment` in the lastrunfile are identical, and the `run_mode` is `agent`. The logic is similar to what was removed in commit 39df438 except we're using `fail_on_404` so `Puppet::Node.indirection.find` will raise instead of returning nil. We also don't clear facts/query_options. Co-authored-by: Josh Cooper <[email protected]>
1 parent 2163522 commit f0214e6

File tree

3 files changed

+150
-32
lines changed

3 files changed

+150
-32
lines changed

lib/puppet/configurer.rb

Lines changed: 59 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,67 @@ 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+
@server_specified_environment = node.environment.name.to_s
517+
518+
if @server_specified_environment != @environment
519+
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 }
520+
end
521+
end
522+
523+
options[:report].add_times(:node_retrieval, node_retr_time)
524+
525+
@server_specified_environment
526+
rescue => detail
527+
Puppet.warning(_("Unable to fetch my node definition, but the agent run will continue:"))
528+
Puppet.warning(detail)
529+
nil
530+
end
531+
end
532+
private :current_server_specified_environment
533+
484534
def send_report(report)
485535
puts report.summary if Puppet[:summarize]
486536
save_last_run_summary(report)

spec/integration/application/agent_spec.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,27 @@ 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+
server.start_server do |port|
647+
Puppet[:serverport] = port
648+
Puppet[:log_level] = 'debug'
649+
650+
expect {
651+
agent.command_line.args << '--test'
652+
agent.run
653+
}.to exit_with(0)
654+
.and output(a_string_matching(%r{Debug: Requesting environment from the server})).to_stdout
655+
656+
Puppet::Application.clear!
657+
658+
expect {
659+
agent.command_line.args << '--test'
660+
agent.run
661+
}.to exit_with(0)
662+
.and output(a_string_matching(%r{Debug: Successfully loaded last environment from the lastrunfile})).to_stdout
663+
end
664+
end
665+
645666
it "switches to 'newenv' environment and retries the run" do
646667
first_run = true
647668
libdir = File.join(my_fixture_dir, 'lib')

spec/unit/configurer_spec.rb

Lines changed: 70 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,6 +1116,9 @@ def expects_neither_new_or_cached_catalog
11161116
converged_environment: #{last_server_specified_environment}
11171117
run_mode: agent
11181118
SUMMARY
1119+
1120+
expect(Puppet::Node.indirection).not_to receive(:find)
1121+
.with(anything, hash_including(:ignore_cache => true, :fail_on_404 => true))
11191122
end
11201123

11211124
it "prefers the environment set via cli" do
@@ -1125,26 +1128,27 @@ def expects_neither_new_or_cached_catalog
11251128
expect(configurer.environment).to eq('usethis')
11261129
end
11271130

1128-
it "prefers the environment set via config" do
1131+
it "prefers the environment set via lastrunfile over config" do
11291132
FileUtils.mkdir_p(Puppet[:confdir])
11301133
set_puppet_conf(Puppet[:confdir], <<~CONF)
11311134
[main]
11321135
environment = usethis
1136+
lastrunfile = #{Puppet[:lastrunfile]}
11331137
CONF
11341138

11351139
Puppet.initialize_settings
11361140
configurer.run
11371141

1138-
expect(configurer.environment).to eq('usethis')
1142+
expect(configurer.environment).to eq(last_server_specified_environment)
11391143
end
11401144

1141-
it "uses environment from Puppet[:environment] if given a catalog" do
1145+
it "uses the environment from Puppet[:environment] if given a catalog" do
11421146
configurer.run(catalog: catalog)
11431147

11441148
expect(configurer.environment).to eq(Puppet[:environment])
11451149
end
11461150

1147-
it "uses environment from Puppet[:environment] if use_cached_catalog = true" do
1151+
it "uses the environment from Puppet[:environment] if use_cached_catalog = true" do
11481152
Puppet[:use_cached_catalog] = true
11491153
expects_cached_catalog_only(catalog)
11501154
configurer.run
@@ -1173,14 +1177,14 @@ def expects_neither_new_or_cached_catalog
11731177
configurer.run
11741178
end
11751179

1176-
it "uses environment from Puppet[:environment] if strict_environment_mode is set" do
1180+
it "uses the environment from Puppet[:environment] if strict_environment_mode is set" do
11771181
Puppet[:strict_environment_mode] = true
11781182
configurer.run
11791183

11801184
expect(configurer.environment).to eq(Puppet[:environment])
11811185
end
11821186

1183-
it "uses environment from Puppet[:environment] if initial_environment is the same as converged_environment" do
1187+
it "uses the environment from Puppet[:environment] if initial_environment is the same as converged_environment" do
11841188
Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)
11851189
---
11861190
version:
@@ -1195,41 +1199,84 @@ def expects_neither_new_or_cached_catalog
11951199

11961200
expect(configurer.environment).to eq(Puppet[:environment])
11971201
end
1202+
end
1203+
end
1204+
1205+
describe "when the last used environment is not available" do
1206+
describe "when the node request succeeds" do
1207+
let(:node_environment) { Puppet::Node::Environment.remote(:salam) }
1208+
let(:node) { double(Puppet::Node, :environment => node_environment) }
1209+
let(:last_server_specified_environment) { 'development' }
1210+
1211+
before do
1212+
allow(Puppet::Node.indirection).to receive(:find)
1213+
allow(Puppet::Node.indirection).to receive(:find)
1214+
.with(anything, hash_including(:ignore_cache => true, :fail_on_404 => true))
1215+
.and_return(node)
1216+
end
11981217

1199-
it "uses environment from Puppet[:environment] if the run mode doesn't match" do
1218+
it "uses the environment from the node request if the run mode doesn't match" do
12001219
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
1220+
---
1221+
version:
1222+
config: 1624882680
1223+
puppet: 6.24.0
1224+
application:
1225+
initial_environment: #{Puppet[:environment]}
1226+
converged_environment: #{last_server_specified_environment}
1227+
run_mode: user
12091228
SUMMARY
12101229
configurer.run
12111230

1212-
expect(configurer.environment).to eq(Puppet[:environment])
1231+
expect(configurer.environment).to eq(node_environment.name.to_s)
12131232
end
12141233

1215-
it "uses environment from Puppet[:environment] if lastrunfile is invalid YAML" do
1234+
it "uses the environment from the node request if lastrunfile does not contain the expected keys" do
12161235
Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)
1217-
Key: 'this is my very very very ' +
1218-
'long string'
1236+
---
1237+
version:
1238+
config: 1624882680
1239+
puppet: 6.24.0
12191240
SUMMARY
12201241
configurer.run
12211242

1222-
expect(configurer.environment).to eq(Puppet[:environment])
1243+
expect(configurer.environment).to eq(node_environment.name.to_s)
12231244
end
12241245

1225-
it "uses environment from Puppet[:environment] if lastrunfile exists but is empty" do
1246+
it "uses the environment from the node request if lastrunfile is invalid YAML" do
1247+
Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)
1248+
Key: 'this is my very very very ' +
1249+
'long string'
1250+
SUMMARY
1251+
configurer.run
1252+
1253+
expect(configurer.environment).to eq(node_environment.name.to_s)
1254+
end
1255+
1256+
it "uses the environment from the node request if lastrunfile exists but is empty" do
12261257
Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', '')
12271258
configurer.run
12281259

1229-
expect(configurer.environment).to eq(Puppet[:environment])
1260+
expect(configurer.environment).to eq(node_environment.name.to_s)
1261+
end
1262+
1263+
it "uses the environment from the node request if the last used one cannot be found" do
1264+
Puppet[:lastrunfile] = tmpfile('last_run_summary.yaml')
1265+
configurer.run
1266+
1267+
expect(configurer.environment).to eq(node_environment.name.to_s)
1268+
end
1269+
end
1270+
1271+
describe "when the node request fails" do
1272+
before do
1273+
allow(Puppet::Node.indirection).to receive(:find).and_call_original
1274+
allow(Puppet::Node.indirection).to receive(:find)
1275+
.with(anything, hash_including(:ignore_cache => true, :fail_on_404 => true))
1276+
.and_raise(Puppet::Error)
12301277
end
12311278

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

0 commit comments

Comments
 (0)