Skip to content

Commit be5a65f

Browse files
committed
(PUP-10942) Don't leak environment state when creating a new instance
Environment state for an expired environment leaked into newly created instances, if the cached loader's `list` or `get_conf` methods had been called prior to expiration. This doesn't occur if `get` is called, because the environment is cached, and the per-environment settings are cleared when `clear` or `clear_all` are called. Clear per-environments settings prior to creating the environment instance.
1 parent 5c5e09e commit be5a65f

File tree

4 files changed

+81
-36
lines changed

4 files changed

+81
-36
lines changed

lib/puppet/environments.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ def get_conf(name)
225225
private
226226

227227
def create_environment(name)
228+
# interpolated modulepaths may be cached from prior environment instances
229+
Puppet.settings.clear_environment_settings(name)
230+
228231
env_symbol = name.intern
229232
setting_values = Puppet.settings.values(env_symbol, Puppet.settings.preferred_run_mode)
230233
env = Puppet::Node::Environment.create(

lib/puppet/file_system/memory_file.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ def self.a_missing_file(path)
77
new(path, :exist? => false, :executable? => false)
88
end
99

10+
def self.a_missing_directory(path)
11+
new(path,
12+
:exist? => false,
13+
:executable? => false,
14+
:directory? => true)
15+
end
16+
1017
def self.a_regular_file_containing(path, content)
1118
new(path, :exist? => true, :executable? => false, :content => content)
1219
end
@@ -18,7 +25,7 @@ def self.an_executable(path)
1825
def self.a_directory(path, children = [])
1926
new(path,
2027
:exist? => true,
21-
:excutable? => true,
28+
:executable? => true,
2229
:directory? => true,
2330
:children => children)
2431
end

lib/puppet/settings/environment_conf.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def self.load_from(path_to_env, global_module_path)
2929
section = config.sections[:main]
3030
rescue Errno::ENOENT
3131
# environment.conf is an optional file
32+
Puppet.debug { "Path to #{path_to_env} does not exist, using default environment.conf" }
3233
end
3334

3435
new(path_to_env, section, global_module_path)

spec/unit/environments_spec.rb

Lines changed: 69 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,30 @@ module PuppetEnvironments
723723

724724
context '#clear_all' do
725725
let(:service) { ReplayExpirationService.new }
726+
let(:envdir) { File.expand_path("envdir") }
727+
let(:default_dir) { File.join(envdir, "cached_env", "modules") }
728+
let(:expected_dir) { File.join(envdir, "cached_env", "site") }
729+
730+
let(:base_dir) do
731+
FS::MemoryFile.a_directory(envdir, [
732+
FS::MemoryFile.a_directory("cached_env", [
733+
FS::MemoryFile.a_missing_file("environment.conf")
734+
])
735+
])
736+
end
737+
738+
let(:updated_dir) do
739+
FS::MemoryFile.a_directory(envdir, [
740+
FS::MemoryFile.a_directory("cached_env", [
741+
FS::MemoryFile.a_directory("site"),
742+
FS::MemoryFile.a_missing_directory("modules"),
743+
FS::MemoryFile.a_regular_file_containing("environment.conf", <<-EOF)
744+
modulepath=site
745+
environment_timeout=unlimited
746+
EOF
747+
])
748+
])
749+
end
726750

727751
it 'evicts all environments' do
728752
with_environment_loaded(service) do |cached|
@@ -734,48 +758,44 @@ module PuppetEnvironments
734758
end
735759
end
736760

737-
it 'clears cached environment settings' do
738-
base_dir = File.expand_path("envdir")
739-
original_envdir = FS::MemoryFile.a_directory(base_dir, [
740-
FS::MemoryFile.a_directory("env3", [
741-
FS::MemoryFile.a_regular_file_containing("environment.conf", <<-EOF)
742-
manifest=/manifest_orig
743-
modulepath=/modules_orig
744-
environment_timeout=60
745-
EOF
746-
]),
747-
])
761+
it "recomputes modulepath if 'get' is called before 'clear_all'" do
762+
cached_loader_from(:filesystem => [base_dir], :directory => base_dir) do |loader|
763+
loader.get(:cached_env)
748764

749-
FS.overlay(original_envdir) do
750-
dir_loader = Puppet::Environments::Directories.new(original_envdir, [])
751-
loader = Puppet::Environments::Cached.new(dir_loader)
752-
Puppet.override(:environments => loader) do
753-
original_env = loader.get("env3") # force the environment.conf to be read
765+
expect(Puppet.settings.value(:modulepath, :cached_env)).to eq(default_dir)
754766

755-
changed_envdir = FS::MemoryFile.a_directory(base_dir, [
756-
FS::MemoryFile.a_directory("env3", [
757-
FS::MemoryFile.a_regular_file_containing("environment.conf", <<-EOF)
758-
manifest=/manifest_changed
759-
modulepath=/modules_changed
760-
environment_timeout=60
761-
EOF
762-
]),
763-
])
767+
FS.overlay(updated_dir) do
768+
loader.clear_all
764769

765-
#Clear all cached environments
770+
expect(loader.get(:cached_env).modulepath).to contain_exactly(expected_dir)
771+
end
772+
end
773+
end
774+
775+
it "recomputes modulepath if 'list' is called before 'clear_all'" do
776+
cached_loader_from(:filesystem => [base_dir], :directory => base_dir) do |loader|
777+
loader.list
778+
779+
expect(Puppet.settings.value(:modulepath, :cached_env)).to eq(default_dir)
780+
781+
FS.overlay(updated_dir) do
766782
loader.clear_all
767783

768-
FS.overlay(changed_envdir) do
769-
changed_env = loader.get("env3")
784+
expect(loader.get(:cached_env).modulepath).to contain_exactly(expected_dir)
785+
end
786+
end
787+
end
770788

771-
expect(original_env).to environment(:env3).
772-
with_manifest(File.expand_path("/manifest_orig")).
773-
with_full_modulepath([File.expand_path("/modules_orig")])
789+
it "recomputes modulepath if 'get_conf' is called before 'clear_all'" do
790+
cached_loader_from(:filesystem => [base_dir], :directory => base_dir) do |loader|
791+
loader.get_conf(:cached_env)
774792

775-
expect(changed_env).to environment(:env3).
776-
with_manifest(File.expand_path("/manifest_changed")).
777-
with_full_modulepath([File.expand_path("/modules_changed")])
778-
end
793+
expect(Puppet.settings.value(:modulepath, :cached_env)).to eq(default_dir)
794+
795+
FS.overlay(updated_dir) do
796+
loader.clear_all
797+
798+
expect(loader.get(:cached_env).modulepath).to contain_exactly(expected_dir)
779799
end
780800
end
781801
end
@@ -861,6 +881,20 @@ module PuppetEnvironments
861881
end
862882
end
863883

884+
def cached_loader_from(options, &block)
885+
FS.overlay(*options[:filesystem]) do
886+
environments = Puppet::Environments::Cached.new(
887+
Puppet::Environments::Directories.new(
888+
options[:directory],
889+
options[:modulepath] || []
890+
)
891+
)
892+
Puppet.override(:environments => environments) do
893+
yield environments
894+
end
895+
end
896+
end
897+
864898
def loader_from(options, &block)
865899
FS.overlay(*options[:filesystem]) do
866900
environments = Puppet::Environments::Directories.new(

0 commit comments

Comments
 (0)