puppet 7.14.0-universal-darwin → 7.17.0-universal-darwin

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CODEOWNERS +1 -1
  3. data/Gemfile.lock +86 -25
  4. data/ext/systemd/puppet.service +1 -1
  5. data/lib/puppet/agent.rb +20 -2
  6. data/lib/puppet/application/agent.rb +3 -13
  7. data/lib/puppet/application/apply.rb +2 -2
  8. data/lib/puppet/application/lookup.rb +24 -28
  9. data/lib/puppet/configurer.rb +7 -3
  10. data/lib/puppet/defaults.rb +11 -2
  11. data/lib/puppet/functions/next.rb +18 -1
  12. data/lib/puppet/functions/tree_each.rb +0 -1
  13. data/lib/puppet/http/client.rb +23 -3
  14. data/lib/puppet/parameter.rb +19 -4
  15. data/lib/puppet/pops/evaluator/deferred_resolver.rb +46 -6
  16. data/lib/puppet/pops/functions/dispatcher.rb +10 -6
  17. data/lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb +7 -6
  18. data/lib/puppet/pops/types/type_mismatch_describer.rb +22 -1
  19. data/lib/puppet/provider/package/puppetserver_gem.rb +7 -16
  20. data/lib/puppet/provider/package/yum.rb +8 -3
  21. data/lib/puppet/provider/user/directoryservice.rb +15 -8
  22. data/lib/puppet/ssl/ssl_provider.rb +75 -19
  23. data/lib/puppet/ssl/state_machine.rb +13 -17
  24. data/lib/puppet/transaction.rb +22 -0
  25. data/lib/puppet/type/exec.rb +1 -1
  26. data/lib/puppet/type/user.rb +3 -0
  27. data/lib/puppet/type.rb +20 -3
  28. data/lib/puppet/util/monkey_patches.rb +0 -2
  29. data/lib/puppet/util.rb +1 -0
  30. data/lib/puppet/version.rb +1 -1
  31. data/lib/puppet.rb +1 -14
  32. data/man/man5/puppet.conf.5 +11 -3
  33. data/man/man8/puppet-agent.8 +2 -2
  34. data/man/man8/puppet-apply.8 +1 -1
  35. data/man/man8/puppet-catalog.8 +1 -1
  36. data/man/man8/puppet-config.8 +1 -1
  37. data/man/man8/puppet-describe.8 +1 -1
  38. data/man/man8/puppet-device.8 +1 -1
  39. data/man/man8/puppet-doc.8 +1 -1
  40. data/man/man8/puppet-epp.8 +1 -1
  41. data/man/man8/puppet-facts.8 +1 -1
  42. data/man/man8/puppet-filebucket.8 +1 -1
  43. data/man/man8/puppet-generate.8 +1 -1
  44. data/man/man8/puppet-help.8 +1 -1
  45. data/man/man8/puppet-lookup.8 +1 -1
  46. data/man/man8/puppet-module.8 +1 -1
  47. data/man/man8/puppet-node.8 +1 -1
  48. data/man/man8/puppet-parser.8 +1 -1
  49. data/man/man8/puppet-plugin.8 +1 -1
  50. data/man/man8/puppet-report.8 +1 -1
  51. data/man/man8/puppet-resource.8 +1 -1
  52. data/man/man8/puppet-script.8 +1 -1
  53. data/man/man8/puppet-ssl.8 +1 -1
  54. data/man/man8/puppet.8 +2 -2
  55. data/spec/integration/application/agent_spec.rb +157 -0
  56. data/spec/integration/application/apply_spec.rb +74 -0
  57. data/spec/integration/application/lookup_spec.rb +64 -59
  58. data/spec/integration/application/resource_spec.rb +6 -2
  59. data/spec/integration/http/client_spec.rb +51 -4
  60. data/spec/lib/puppet_spec/https.rb +1 -1
  61. data/spec/lib/puppet_spec/puppetserver.rb +39 -2
  62. data/spec/unit/agent_spec.rb +6 -2
  63. data/spec/unit/application/agent_spec.rb +26 -16
  64. data/spec/unit/configurer_spec.rb +34 -3
  65. data/spec/unit/confiner_spec.rb +6 -6
  66. data/spec/unit/daemon_spec.rb +2 -11
  67. data/spec/unit/http/client_spec.rb +18 -0
  68. data/spec/unit/pops/evaluator/deferred_resolver_spec.rb +26 -0
  69. data/spec/unit/pops/loaders/loaders_spec.rb +1 -1
  70. data/spec/unit/pops/types/type_mismatch_describer_spec.rb +167 -1
  71. data/spec/unit/provider/package/puppetserver_gem_spec.rb +2 -2
  72. data/spec/unit/provider/user/directoryservice_spec.rb +1 -1
  73. data/spec/unit/ssl/ssl_provider_spec.rb +75 -1
  74. data/spec/unit/ssl/state_machine_spec.rb +1 -0
  75. data/spec/unit/util/windows_spec.rb +23 -0
  76. data/tasks/generate_cert_fixtures.rake +5 -4
  77. metadata +5 -3
@@ -7,7 +7,7 @@ describe 'lookup' do
7
7
  include PuppetSpec::Files
8
8
 
9
9
  context 'with an environment' do
10
- let(:fqdn) { Puppet.runtime[:facter].value(:fqdn) }
10
+ let(:fqdn) { Puppet[:certname] }
11
11
  let(:env_name) { 'spec' }
12
12
  let(:env_dir) { tmpdir('environments') }
13
13
  let(:environment_files) do
@@ -43,12 +43,10 @@ describe 'lookup' do
43
43
  end
44
44
 
45
45
  let(:app) { Puppet::Application[:lookup] }
46
- let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_env_dir, env_name, 'modules')]) }
47
- let(:environments) { Puppet::Environments::Directories.new(populated_env_dir, []) }
48
46
  let(:facts) { Puppet::Node::Facts.new("facts", {'my_fact' => 'my_fact_value'}) }
49
47
  let(:cert) { pem_content('oid.pem') }
50
48
 
51
- let(:node) { Puppet::Node.new('testnode', :facts => facts, :environment => env) }
49
+ let(:node) { Puppet::Node.new('testnode', :facts => facts) }
52
50
  let(:populated_env_dir) do
53
51
  dir_contained_in(env_dir, environment_files)
54
52
  env_dir
@@ -57,75 +55,72 @@ describe 'lookup' do
57
55
  before do
58
56
  stub_request(:get, "https://puppet:8140/puppet-ca/v1/certificate/#{fqdn}").to_return(body: cert)
59
57
  allow(Puppet::Node::Facts.indirection).to receive(:find).and_return(facts)
60
- end
61
58
 
62
- def lookup(key, options = {}, explain = false)
63
- key = [key] unless key.is_a?(Array)
64
- allow(app.command_line).to receive(:args).and_return(key)
65
- if explain
66
- app.options[:explain] = true
67
- app.options[:render_as] = :s
68
- else
69
- app.options[:render_as] = :json
70
- end
71
- options.each_pair { |k, v| app.options[k] = v }
72
- capture = StringIO.new
73
- saved_stdout = $stdout
74
- begin
75
- $stdout = capture
76
- expect { app.run_command }.to exit_with(0)
77
- ensure
78
- $stdout = saved_stdout
79
- end
80
- out = capture.string.strip
81
- if explain
82
- out
83
- else
84
- out.empty? ? nil : JSON.parse("[#{out}]")[0]
85
- end
86
- end
59
+ Puppet[:environment] = env_name
60
+ Puppet[:environmentpath] = populated_env_dir
87
61
 
88
- def explain(key, options = {})
89
- lookup(key, options, true)
62
+ http = Puppet::HTTP::Client.new(ssl_context: Puppet::SSL::SSLProvider.new.create_insecure_context)
63
+ Puppet.runtime[:http] = http
90
64
  end
91
65
 
92
- around(:each) do |example|
93
- Puppet.override(:environments => environments, :current_environment => env) do
94
- example.run
95
- end
66
+ def expect_lookup_with_output(exitcode, out)
67
+ expect { app.run }.to exit_with(exitcode).and output(out).to_stdout
96
68
  end
97
69
 
98
70
  it 'finds data in the environment' do
99
- expect(lookup('a')).to eql('value a')
71
+ app.command_line.args << 'a'
72
+ expect_lookup_with_output(0, /value a/)
100
73
  end
101
74
 
102
- it 'loads trusted information from the node certificate' do
103
- allow(Puppet).to receive(:override).and_call_original
104
- expect(Puppet).to receive(:override).with(trusted_information: an_object_having_attributes(
105
- certname: fqdn,
106
- extensions: { "1.3.6.1.4.1.34380.1.2.1.1" => "somevalue" }))
75
+ it "resolves hiera data using a top-level node parameter" do
76
+ File.write(File.join(env_dir, env_name, 'hiera.yaml'), <<~YAML)
77
+ ---
78
+ version: 5
79
+ hierarchy:
80
+ - name: "Per Node"
81
+ data_hash: yaml_data
82
+ path: "%{my_fact}.yaml"
83
+ YAML
84
+
85
+ File.write(File.join(env_dir, env_name, 'data', "my_fact_value.yaml"), <<~YAML)
86
+ ---
87
+ a: value from per node data
88
+ YAML
89
+
90
+ app.command_line.args << 'a'
91
+ expect_lookup_with_output(0, /--- value from per node data/)
92
+ end
107
93
 
94
+ it 'loads trusted information from the node certificate' do
108
95
  Puppet.settings[:node_terminus] = 'exec'
109
- expect_any_instance_of(Puppet::Node::Exec).to receive(:find).and_return(node)
110
- lookup('a', :compile => true)
96
+ expect_any_instance_of(Puppet::Node::Exec).to receive(:find) do |args|
97
+ info = Puppet.lookup(:trusted_information)
98
+ expect(info.certname).to eq(fqdn)
99
+ expect(info.extensions).to eq({ "1.3.6.1.4.1.34380.1.2.1.1" => "somevalue" })
100
+ end.and_return(node)
101
+
102
+ app.command_line.args << 'a' << '--compile'
103
+ expect_lookup_with_output(0, /--- value a/)
111
104
  end
112
105
 
113
106
  it 'loads external facts when running without --node' do
114
107
  expect(Puppet::Util).not_to receive(:skip_external_facts)
115
108
  expect(Facter).not_to receive(:load_external)
116
- lookup('a')
109
+
110
+ app.command_line.args << 'a'
111
+ expect_lookup_with_output(0, /--- value a/)
117
112
  end
118
113
 
119
114
  describe 'when using --node' do
120
115
  let(:fqdn) { 'random_node' }
121
116
 
122
117
  it 'skips loading of external facts' do
123
- app.options[:node] = fqdn
118
+ app.command_line.args << 'a' << '--node' << fqdn
124
119
 
125
120
  expect(Puppet::Node::Facts.indirection).to receive(:find).and_return(facts)
126
- expect(Facter).to receive(:load_external).once.with(false)
127
- expect(Facter).to receive(:load_external).once.with(true)
128
- lookup('a')
121
+ expect(Facter).to receive(:load_external).twice.with(false)
122
+ expect(Facter).to receive(:load_external).twice.with(true)
123
+ expect_lookup_with_output(0, /--- value a/)
129
124
  end
130
125
  end
131
126
 
@@ -133,29 +128,32 @@ describe 'lookup' do
133
128
  require 'puppet/indirector/node/exec'
134
129
  require 'puppet/indirector/node/plain'
135
130
 
136
- let(:node) { Puppet::Node.new('testnode', :facts => facts, :environment => env) }
131
+ let(:node) { Puppet::Node.new('testnode', :facts => facts) }
137
132
 
138
133
  it ':plain without --compile' do
139
134
  Puppet.settings[:node_terminus] = 'exec'
140
135
  expect_any_instance_of(Puppet::Node::Plain).to receive(:find).and_return(node)
141
136
  expect_any_instance_of(Puppet::Node::Exec).not_to receive(:find)
142
- expect(lookup('a')).to eql('value a')
137
+
138
+ app.command_line.args << 'a'
139
+ expect_lookup_with_output(0, /--- value a/)
143
140
  end
144
141
 
145
142
  it 'configured in Puppet settings with --compile' do
146
143
  Puppet.settings[:node_terminus] = 'exec'
147
144
  expect_any_instance_of(Puppet::Node::Plain).not_to receive(:find)
148
145
  expect_any_instance_of(Puppet::Node::Exec).to receive(:find).and_return(node)
149
- expect(lookup('a', :compile => true)).to eql('value a')
146
+
147
+ app.command_line.args << 'a' << '--compile'
148
+ expect_lookup_with_output(0, /--- value a/)
150
149
  end
151
150
  end
152
151
 
153
152
  context 'configured with the wrong environment' do
154
- let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_env_dir, env_name, 'modules')]) }
155
153
  it 'does not find data in non-existing environment' do
156
- Puppet.override(:environments => environments, :current_environment => 'someother') do
157
- expect(lookup('a', {}, true)).to match(/did not find a value for the name 'a'/)
158
- end
154
+ Puppet[:environment] = 'doesntexist'
155
+ app.command_line.args << 'a'
156
+ expect { app.run }.to raise_error(Puppet::Environments::EnvironmentNotFound, /Could not find a directory environment named 'doesntexist'/)
159
157
  end
160
158
  end
161
159
 
@@ -200,15 +198,22 @@ describe 'lookup' do
200
198
  end
201
199
 
202
200
  it 'finds data in the module' do
203
- expect(lookup('mod_a::b')).to eql('value mod_a::b (from mod_a)')
201
+ app.command_line.args << 'mod_a::b'
202
+ expect_lookup_with_output(0, /value mod_a::b \(from mod_a\)/)
204
203
  end
205
204
 
206
205
  it 'finds quoted keys in the module' do
207
- expect(lookup('"mod_a::a.quoted.key"')).to eql('value mod_a::a.quoted.key (from mod_a)')
206
+ app.command_line.args << "'mod_a::a.quoted.key'"
207
+ expect_lookup_with_output(0, /value mod_a::a.quoted.key \(from mod_a\)/)
208
208
  end
209
209
 
210
210
  it 'merges hashes from environment and module when merge strategy hash is used' do
211
- expect(lookup('mod_a::hash_a', :merge => 'hash')).to eql({'a' => 'value mod_a::hash_a.a (from environment)', 'b' => 'value mod_a::hash_a.b (from mod_a)'})
211
+ app.command_line.args << 'mod_a::hash_a' << '--merge' << 'hash'
212
+ expect_lookup_with_output(0, <<~END)
213
+ ---
214
+ a: value mod_a::hash_a.a (from environment)
215
+ b: value mod_a::hash_a.b (from mod_a)
216
+ END
212
217
  end
213
218
  end
214
219
  end
@@ -28,14 +28,18 @@ describe "puppet resource", unless: Puppet::Util::Platform.jruby? do
28
28
  end
29
29
 
30
30
  it 'lists types from the default environment' do
31
+ begin
31
32
  modulepath = File.join(Puppet[:codedir], 'modules', 'test', 'lib', 'puppet', 'type')
32
33
  FileUtils.mkdir_p(modulepath)
33
- File.write(File.join(modulepath, 'test.rb'), 'Puppet::Type.newtype(:test)')
34
+ File.write(File.join(modulepath, 'test_resource_spec.rb'), 'Puppet::Type.newtype(:test_resource_spec)')
34
35
  resource.command_line.args = ['--types']
35
36
 
36
37
  expect {
37
38
  resource.run
38
- }.to exit_with(0).and output(/test/).to_stdout
39
+ }.to exit_with(0).and output(/test_resource_spec/).to_stdout
40
+ ensure
41
+ Puppet::Type.rmtype(:test_resource_spec)
42
+ end
39
43
  end
40
44
  end
41
45
 
@@ -77,7 +77,13 @@ describe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do
77
77
  }
78
78
  }
79
79
 
80
- it "mutually authenticates the connection" do
80
+ let(:cert_file) do
81
+ res = tmpfile('cert_file')
82
+ File.write(res, https_server.ca_cert)
83
+ res
84
+ end
85
+
86
+ it "mutually authenticates the connection using an explicit context" do
81
87
  client_context = ssl_provider.create_context(
82
88
  cacerts: [https_server.ca_cert], crls: [https_server.ca_crl],
83
89
  client_cert: https_server.server_cert, private_key: https_server.server_key
@@ -88,6 +94,47 @@ describe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do
88
94
  expect(res).to be_success
89
95
  end
90
96
  end
97
+
98
+ it "mutually authenticates the connection when the client and server certs are issued from different CAs" do
99
+ # this is the client cert's CA, key and cert
100
+ Puppet[:localcacert] = fixtures('ssl/unknown-ca.pem')
101
+ Puppet[:hostprivkey] = fixtures('ssl/unknown-127.0.0.1-key.pem')
102
+ Puppet[:hostcert] = fixtures('ssl/unknown-127.0.0.1.pem')
103
+
104
+ # this is the server cert's CA that the client needs in order to authenticate the server
105
+ Puppet[:ssl_trust_store] = fixtures('ssl/ca.pem')
106
+
107
+ # need to pass both the client and server CAs. The former is needed so the server can authenticate our client cert
108
+ https_server = PuppetSpec::HTTPSServer.new(ca_cert: [cert_fixture('ca.pem'), cert_fixture('unknown-ca.pem')])
109
+ https_server.start_server(ctx_proc: ctx_proc) do |port|
110
+ res = client.get(URI("https://127.0.0.1:#{port}"), options: {include_system_store: true})
111
+ expect(res).to be_success
112
+ end
113
+ end
114
+
115
+ it "connects when the server's CA is in the system store and the connection is mutually authenticated using create_context" do
116
+ Puppet::Util.withenv("SSL_CERT_FILE" => cert_file) do
117
+ client_context = ssl_provider.create_context(
118
+ cacerts: [], crls: [],
119
+ client_cert: https_server.server_cert, private_key: https_server.server_key,
120
+ revocation: false, include_system_store: true
121
+ )
122
+ https_server.start_server(ctx_proc: ctx_proc) do |port|
123
+ res = client.get(URI("https://127.0.0.1:#{port}"), options: {ssl_context: client_context})
124
+ expect(res).to be_success
125
+ end
126
+ end
127
+ end
128
+
129
+ it "connects when the server's CA is in the system store and the connection is mutually authenticated using load_context" do
130
+ Puppet::Util.withenv("SSL_CERT_FILE" => cert_file) do
131
+ client_context = ssl_provider.load_context(revocation: false, include_system_store: true)
132
+ https_server.start_server(ctx_proc: ctx_proc) do |port|
133
+ res = client.get(URI("https://127.0.0.1:#{port}"), options: {ssl_context: client_context})
134
+ expect(res).to be_success
135
+ end
136
+ end
137
+ end
91
138
  end
92
139
 
93
140
  context "with a system trust store" do
@@ -102,12 +149,12 @@ describe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do
102
149
 
103
150
  it "connects when the server's CA is in the system store" do
104
151
  # create a temp cacert bundle
105
- ssl_file = tmpfile('systemstore')
106
- File.write(ssl_file, https_server.ca_cert)
152
+ cert_file = tmpfile('cert_file')
153
+ File.write(cert_file, https_server.ca_cert)
107
154
 
108
155
  # override path to system cacert bundle, this must be done before
109
156
  # the SSLContext is created and the call to X509::Store.set_default_paths
110
- Puppet::Util.withenv("SSL_CERT_FILE" => ssl_file) do
157
+ Puppet::Util.withenv("SSL_CERT_FILE" => cert_file) do
111
158
  system_context = ssl_provider.create_system_context(cacerts: [])
112
159
  https_server.start_server do |port|
113
160
  res = client.get(URI("https://127.0.0.1:#{port}"), options: {ssl_context: system_context})
@@ -40,7 +40,7 @@ class PuppetSpec::HTTPSServer
40
40
 
41
41
  IO.pipe {|stop_pipe_r, stop_pipe_w|
42
42
  store = OpenSSL::X509::Store.new
43
- store.add_cert(@ca_cert)
43
+ Array(@ca_cert).each { |c| store.add_cert(c) }
44
44
  store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
45
45
  ctx = OpenSSL::SSL::SSLContext.new
46
46
  ctx.cert_store = store
@@ -72,6 +72,40 @@ class PuppetSpec::Puppetserver
72
72
  end
73
73
  end
74
74
 
75
+ class CertificateServlet < WEBrick::HTTPServlet::AbstractServlet
76
+ def initialize(server, ca_cert)
77
+ super(server)
78
+ @ca_cert = ca_cert
79
+ end
80
+
81
+ def do_GET request, response
82
+ if request.path =~ %r{/puppet-ca/v1/certificate/ca$}
83
+ response['Content-Type'] = 'text/plain'
84
+ response.body = @ca_cert.to_pem
85
+ else
86
+ response.status = 404
87
+ end
88
+ end
89
+ end
90
+
91
+ class CertificateRevocationListServlet < WEBrick::HTTPServlet::AbstractServlet
92
+ def initialize(server, crl)
93
+ super(server)
94
+ @crl = crl
95
+ end
96
+
97
+ def do_GET request, response
98
+ response['Content-Type'] = 'text/plain'
99
+ response.body = @crl.to_pem
100
+ end
101
+ end
102
+
103
+ class CertificateRequestServlet < WEBrick::HTTPServlet::AbstractServlet
104
+ def do_PUT request, response
105
+ response.status = 404
106
+ end
107
+ end
108
+
75
109
  def initialize
76
110
  @ca_cert = cert_fixture('ca.pem')
77
111
  @ca_crl = crl_fixture('crl.pem')
@@ -125,15 +159,18 @@ class PuppetSpec::Puppetserver
125
159
  register_mount('/puppet/v3/static_file_content', mounts[:static_file_content], StaticFileContentServlet)
126
160
  register_mount('/puppet/v3/report', mounts[:report], ReportServlet)
127
161
  register_mount('/puppet/v3/file_bucket_file', mounts[:filebucket], FilebucketServlet)
162
+ register_mount('/puppet-ca/v1/certificate', mounts[:certificate], CertificateServlet, @ca_cert)
163
+ register_mount('/puppet-ca/v1/certificate_revocation_list', mounts[:certificate_revocation_list], CertificateRevocationListServlet, @ca_crl)
164
+ register_mount('/puppet-ca/v1/certificate_request', mounts[:certificate_request], CertificateRequestServlet)
128
165
  end
129
166
 
130
- def register_mount(path, user_proc, default_servlet)
167
+ def register_mount(path, user_proc, default_servlet, *args)
131
168
  handler = if user_proc
132
169
  WEBrick::HTTPServlet::ProcHandler.new(user_proc)
133
170
  else
134
171
  default_servlet
135
172
  end
136
- @https.mount(path, handler)
173
+ @https.mount(path, handler, *args)
137
174
  end
138
175
 
139
176
  def upload_directory
@@ -36,6 +36,10 @@ describe Puppet::Agent do
36
36
  end
37
37
  end
38
38
  end
39
+
40
+ ssl_context = Puppet::SSL::SSLContext.new
41
+ machine = instance_double("Puppet::SSL::StateMachine", ensure_client_certificate: ssl_context)
42
+ allow(Puppet::SSL::StateMachine).to receive(:new).and_return(machine)
39
43
  end
40
44
 
41
45
  after do
@@ -195,7 +199,7 @@ describe Puppet::Agent do
195
199
  @agent.run
196
200
  end
197
201
 
198
- it "should inform that a run is already in progres and try to run every X seconds if waitforlock is used" do
202
+ it "should inform that a run is already in progress and try to run every X seconds if waitforlock is used" do
199
203
  # so the locked file exists
200
204
  allow(File).to receive(:file?).and_return(true)
201
205
  # so we don't have to wait again for the run to exit (default maxwaitforcert is 60)
@@ -224,7 +228,7 @@ describe Puppet::Agent do
224
228
  @agent.run
225
229
  end
226
230
  end
227
-
231
+
228
232
  describe "when should_fork is true", :if => Puppet.features.posix? && RUBY_PLATFORM != 'java' do
229
233
  before do
230
234
  @agent = Puppet::Agent.new(AgentTestClient, true)
@@ -4,6 +4,11 @@ require 'puppet/agent'
4
4
  require 'puppet/application/agent'
5
5
  require 'puppet/daemon'
6
6
 
7
+ class TestAgentClientClass
8
+ def initialize(transaction_uuid = nil, job_id = nil); end
9
+ def run(options = {}); end
10
+ end
11
+
7
12
  describe Puppet::Application::Agent do
8
13
  include PuppetSpec::Files
9
14
 
@@ -12,13 +17,20 @@ describe Puppet::Application::Agent do
12
17
  before :each do
13
18
  @puppetd = Puppet::Application[:agent]
14
19
 
15
- @agent = double('agent')
20
+ @client = TestAgentClientClass.new
21
+ allow(TestAgentClientClass).to receive(:new).and_return(@client)
22
+
23
+ @agent = Puppet::Agent.new(TestAgentClientClass, false)
16
24
  allow(Puppet::Agent).to receive(:new).and_return(@agent)
17
25
 
18
- @daemon = Puppet::Daemon.new(@agent, nil)
26
+ Puppet[:pidfile] = tmpfile('pidfile')
27
+ @daemon = Puppet::Daemon.new(@agent, Puppet::Util::Pidlock.new(Puppet[:pidfile]))
19
28
  allow(@daemon).to receive(:daemonize)
20
- allow(@daemon).to receive(:start)
21
29
  allow(@daemon).to receive(:stop)
30
+ # simulate one run so we don't infinite looptwo runs of the agent, then return so we don't infinite loop
31
+ allow(@daemon).to receive(:run_event_loop) do
32
+ @agent.run(splay: false)
33
+ end
22
34
  allow(Puppet::Daemon).to receive(:new).and_return(@daemon)
23
35
  Puppet[:daemonize] = false
24
36
 
@@ -92,10 +104,6 @@ describe Puppet::Application::Agent do
92
104
  end
93
105
 
94
106
  describe "when handling options" do
95
- before do
96
- allow(@puppetd.command_line).to receive(:args).and_return([])
97
- end
98
-
99
107
  [:enable, :debug, :fqdn, :test, :verbose, :digest].each do |option|
100
108
  it "should declare handle_#{option} method" do
101
109
  expect(@puppetd).to respond_to("handle_#{option}".to_sym)
@@ -127,32 +135,34 @@ describe Puppet::Application::Agent do
127
135
  end
128
136
 
129
137
  it "should set waitforcert to 0 with --onetime and if --waitforcert wasn't given" do
130
- allow(@agent).to receive(:run).and_return(2)
138
+ allow(@client).to receive(:run).and_return(2)
131
139
  Puppet[:onetime] = true
132
140
 
133
- expect(Puppet::SSL::StateMachine).to receive(:new).with(waitforcert: 0).and_return(machine)
141
+ expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 0)).and_return(machine)
134
142
 
135
143
  expect { execute_agent }.to exit_with 0
136
144
  end
137
145
 
138
146
  it "should use supplied waitforcert when --onetime is specified" do
139
- allow(@agent).to receive(:run).and_return(2)
147
+ allow(@client).to receive(:run).and_return(2)
140
148
  Puppet[:onetime] = true
141
149
  @puppetd.handle_waitforcert(60)
142
150
 
143
- expect(Puppet::SSL::StateMachine).to receive(:new).with(waitforcert: 60).and_return(machine)
151
+ expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 60)).and_return(machine)
144
152
 
145
153
  expect { execute_agent }.to exit_with 0
146
154
  end
147
155
 
148
156
  it "should use a default value for waitforcert when --onetime and --waitforcert are not specified" do
149
- expect(Puppet::SSL::StateMachine).to receive(:new).with(waitforcert: 120).and_return(machine)
157
+ allow(@client).to receive(:run).and_return(2)
158
+
159
+ expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 120)).and_return(machine)
150
160
 
151
161
  execute_agent
152
162
  end
153
163
 
154
164
  it "should register ssl OIDs" do
155
- expect(Puppet::SSL::StateMachine).to receive(:new).with(waitforcert: 120).and_return(double(ensure_client_certificate: nil))
165
+ expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 120)).and_return(machine)
156
166
  expect(Puppet::SSL::Oids).to receive(:register_puppet_oids)
157
167
 
158
168
  execute_agent
@@ -161,7 +171,7 @@ describe Puppet::Application::Agent do
161
171
  it "should use the waitforcert setting when checking for a signed certificate" do
162
172
  Puppet[:waitforcert] = 10
163
173
 
164
- expect(Puppet::SSL::StateMachine).to receive(:new).with(waitforcert: 10).and_return(machine)
174
+ expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 10)).and_return(machine)
165
175
 
166
176
  execute_agent
167
177
  end
@@ -413,9 +423,9 @@ describe Puppet::Application::Agent do
413
423
  end
414
424
 
415
425
  it "should wait for a certificate" do
416
- @puppetd.options[:waitforcert] = 123
426
+ Puppet[:waitforcert] = 123
417
427
 
418
- expect(Puppet::SSL::StateMachine).to receive(:new).with(waitforcert: 123).and_return(machine)
428
+ expect(Puppet::SSL::StateMachine).to receive(:new).with(hash_including(waitforcert: 123)).and_return(machine)
419
429
 
420
430
  execute_agent
421
431
  end
@@ -9,9 +9,6 @@ describe Puppet::Configurer do
9
9
  Puppet[:report] = true
10
10
 
11
11
  catalog.add_resource(resource)
12
- allow_any_instance_of(described_class).to(
13
- receive(:valid_server_environment?).and_return(true)
14
- )
15
12
 
16
13
  Puppet[:lastrunfile] = file_containing('last_run_summary.yaml', <<~SUMMARY)
17
14
  ---
@@ -78,10 +75,44 @@ describe Puppet::Configurer do
78
75
  end
79
76
  end
80
77
 
78
+ describe "when executing a catalog run without stubbing valid_server_environment?" do
79
+ before do
80
+ Puppet::Resource::Catalog.indirection.terminus_class = :rest
81
+ allow(Puppet::Resource::Catalog.indirection).to receive(:find).and_return(catalog)
82
+ end
83
+
84
+ it 'skips initial plugin sync if environment is not found and no strict_environment_mode' do
85
+ body = "{\"message\":\"Not Found: Could not find environment 'fasdfad'\",\"issue_kind\":\"RUNTIME_ERROR\"}"
86
+ stub_request(:get, %r{/puppet/v3/file_metadatas/plugins?}).to_return(
87
+ status: 404, body: body, headers: {'Content-Type' => 'application/json'}
88
+ )
89
+
90
+ configurer.run(:pluginsync => true)
91
+
92
+ expect(@logs).to include(an_object_having_attributes(level: :notice, message: %r{Environment 'production' not found on server, skipping initial pluginsync.}))
93
+ expect(@logs).to include(an_object_having_attributes(level: :notice, message: /Applied catalog in .* seconds/))
94
+ end
95
+
96
+ it 'if strict_environment_mode is set and environment is not found, aborts the puppet run' do
97
+ Puppet[:strict_environment_mode] = true
98
+ body = "{\"message\":\"Not Found: Could not find environment 'fasdfad'\",\"issue_kind\":\"RUNTIME_ERROR\"}"
99
+ stub_request(:get, %r{/puppet/v3/file_metadatas/plugins?}).to_return(
100
+ status: 404, body: body, headers: {'Content-Type' => 'application/json'}
101
+ )
102
+
103
+ configurer.run(:pluginsync => true)
104
+
105
+ expect(@logs).to include(an_object_having_attributes(level: :err, message: %r{Failed to apply catalog: Environment 'production' not found on server, aborting run.}))
106
+ end
107
+ end
108
+
81
109
  describe "when executing a catalog run" do
82
110
  before do
83
111
  Puppet::Resource::Catalog.indirection.terminus_class = :rest
84
112
  allow(Puppet::Resource::Catalog.indirection).to receive(:find).and_return(catalog)
113
+ allow_any_instance_of(described_class).to(
114
+ receive(:valid_server_environment?).and_return(true)
115
+ )
85
116
  end
86
117
 
87
118
  it "downloads plugins when told" do
@@ -3,6 +3,8 @@ require 'spec_helper'
3
3
  require 'puppet/confiner'
4
4
 
5
5
  describe Puppet::Confiner do
6
+ let(:coll) { Puppet::ConfineCollection.new('') }
7
+
6
8
  before do
7
9
  @object = Object.new
8
10
  @object.extend(Puppet::Confiner)
@@ -21,7 +23,6 @@ describe Puppet::Confiner do
21
23
  end
22
24
 
23
25
  it "should delegate its confine method to its confine collection" do
24
- coll = double('collection')
25
26
  allow(@object).to receive(:confine_collection).and_return(coll)
26
27
  expect(coll).to receive(:confine).with(:foo => :bar, :bee => :baz)
27
28
  @object.confine(:foo => :bar, :bee => :baz)
@@ -39,22 +40,21 @@ describe Puppet::Confiner do
39
40
 
40
41
  describe "when testing suitability" do
41
42
  before do
42
- @coll = double('collection')
43
- allow(@object).to receive(:confine_collection).and_return(@coll)
43
+ allow(@object).to receive(:confine_collection).and_return(coll)
44
44
  end
45
45
 
46
46
  it "should return true if the confine collection is valid" do
47
- expect(@coll).to receive(:valid?).and_return(true)
47
+ expect(coll).to receive(:valid?).and_return(true)
48
48
  expect(@object).to be_suitable
49
49
  end
50
50
 
51
51
  it "should return false if the confine collection is invalid" do
52
- expect(@coll).to receive(:valid?).and_return(false)
52
+ expect(coll).to receive(:valid?).and_return(false)
53
53
  expect(@object).not_to be_suitable
54
54
  end
55
55
 
56
56
  it "should return the summary of the confine collection if a long result is asked for" do
57
- expect(@coll).to receive(:summary).and_return("myresult")
57
+ expect(coll).to receive(:summary).and_return("myresult")
58
58
  expect(@object.suitable?(false)).to eq("myresult")
59
59
  end
60
60
  end
@@ -1,6 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'puppet/daemon'
3
3
  require 'puppet/agent'
4
+ require 'puppet/configurer'
4
5
 
5
6
  def without_warnings
6
7
  flag = $VERBOSE
@@ -9,12 +10,6 @@ def without_warnings
9
10
  $VERBOSE = flag
10
11
  end
11
12
 
12
- class TestClient
13
- def lockfile_path
14
- "/dev/null"
15
- end
16
- end
17
-
18
13
  describe Puppet::Daemon, :unless => Puppet::Util::Platform.windows? do
19
14
  include PuppetSpec::Files
20
15
 
@@ -26,7 +21,7 @@ describe Puppet::Daemon, :unless => Puppet::Util::Platform.windows? do
26
21
  end
27
22
  end
28
23
 
29
- let(:agent) { Puppet::Agent.new(TestClient.new, false) }
24
+ let(:agent) { Puppet::Agent.new(Puppet::Configurer, false) }
30
25
  let(:server) { double("Server", :start => nil, :wait_for_shutdown => nil) }
31
26
 
32
27
  let(:pidfile) { double("PidFile", :lock => true, :unlock => true, :file_path => 'fake.pid') }
@@ -131,10 +126,6 @@ describe Puppet::Daemon, :unless => Puppet::Util::Platform.windows? do
131
126
  end
132
127
 
133
128
  describe "when reloading" do
134
- it "should do nothing if no agent is configured" do
135
- daemon.reload
136
- end
137
-
138
129
  it "should do nothing if the agent is running" do
139
130
  expect(agent).to receive(:run).with({:splay => false}).and_raise(Puppet::LockError, 'Failed to aquire lock')
140
131
  expect(Puppet).to receive(:notice).with('Not triggering already-running agent')