puppet 7.14.0-x64-mingw32 → 7.17.0-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CODEOWNERS +1 -1
- data/Gemfile.lock +86 -25
- data/ext/systemd/puppet.service +1 -1
- data/lib/puppet/agent.rb +20 -2
- data/lib/puppet/application/agent.rb +3 -13
- data/lib/puppet/application/apply.rb +2 -2
- data/lib/puppet/application/lookup.rb +24 -28
- data/lib/puppet/configurer.rb +7 -3
- data/lib/puppet/defaults.rb +11 -2
- data/lib/puppet/functions/next.rb +18 -1
- data/lib/puppet/functions/tree_each.rb +0 -1
- data/lib/puppet/http/client.rb +23 -3
- data/lib/puppet/parameter.rb +19 -4
- data/lib/puppet/pops/evaluator/deferred_resolver.rb +46 -6
- data/lib/puppet/pops/functions/dispatcher.rb +10 -6
- data/lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb +7 -6
- data/lib/puppet/pops/types/type_mismatch_describer.rb +22 -1
- data/lib/puppet/provider/package/puppetserver_gem.rb +7 -16
- data/lib/puppet/provider/package/yum.rb +8 -3
- data/lib/puppet/provider/user/directoryservice.rb +15 -8
- data/lib/puppet/ssl/ssl_provider.rb +75 -19
- data/lib/puppet/ssl/state_machine.rb +13 -17
- data/lib/puppet/transaction.rb +22 -0
- data/lib/puppet/type/exec.rb +1 -1
- data/lib/puppet/type/user.rb +3 -0
- data/lib/puppet/type.rb +20 -3
- data/lib/puppet/util/monkey_patches.rb +0 -2
- data/lib/puppet/util.rb +1 -0
- data/lib/puppet/version.rb +1 -1
- data/lib/puppet.rb +1 -14
- data/man/man5/puppet.conf.5 +11 -3
- data/man/man8/puppet-agent.8 +2 -2
- data/man/man8/puppet-apply.8 +1 -1
- data/man/man8/puppet-catalog.8 +1 -1
- data/man/man8/puppet-config.8 +1 -1
- data/man/man8/puppet-describe.8 +1 -1
- data/man/man8/puppet-device.8 +1 -1
- data/man/man8/puppet-doc.8 +1 -1
- data/man/man8/puppet-epp.8 +1 -1
- data/man/man8/puppet-facts.8 +1 -1
- data/man/man8/puppet-filebucket.8 +1 -1
- data/man/man8/puppet-generate.8 +1 -1
- data/man/man8/puppet-help.8 +1 -1
- data/man/man8/puppet-lookup.8 +1 -1
- data/man/man8/puppet-module.8 +1 -1
- data/man/man8/puppet-node.8 +1 -1
- data/man/man8/puppet-parser.8 +1 -1
- data/man/man8/puppet-plugin.8 +1 -1
- data/man/man8/puppet-report.8 +1 -1
- data/man/man8/puppet-resource.8 +1 -1
- data/man/man8/puppet-script.8 +1 -1
- data/man/man8/puppet-ssl.8 +1 -1
- data/man/man8/puppet.8 +2 -2
- data/spec/integration/application/agent_spec.rb +157 -0
- data/spec/integration/application/apply_spec.rb +74 -0
- data/spec/integration/application/lookup_spec.rb +64 -59
- data/spec/integration/application/resource_spec.rb +6 -2
- data/spec/integration/http/client_spec.rb +51 -4
- data/spec/lib/puppet_spec/https.rb +1 -1
- data/spec/lib/puppet_spec/puppetserver.rb +39 -2
- data/spec/unit/agent_spec.rb +6 -2
- data/spec/unit/application/agent_spec.rb +26 -16
- data/spec/unit/configurer_spec.rb +34 -3
- data/spec/unit/confiner_spec.rb +6 -6
- data/spec/unit/daemon_spec.rb +2 -11
- data/spec/unit/http/client_spec.rb +18 -0
- data/spec/unit/pops/evaluator/deferred_resolver_spec.rb +26 -0
- data/spec/unit/pops/loaders/loaders_spec.rb +1 -1
- data/spec/unit/pops/types/type_mismatch_describer_spec.rb +167 -1
- data/spec/unit/provider/package/puppetserver_gem_spec.rb +2 -2
- data/spec/unit/provider/user/directoryservice_spec.rb +1 -1
- data/spec/unit/ssl/ssl_provider_spec.rb +75 -1
- data/spec/unit/ssl/state_machine_spec.rb +1 -0
- data/spec/unit/util/windows_spec.rb +23 -0
- data/tasks/generate_cert_fixtures.rake +5 -4
- 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
|
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
|
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
|
-
|
63
|
-
|
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
|
-
|
89
|
-
|
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
|
-
|
93
|
-
|
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
|
-
|
71
|
+
app.command_line.args << 'a'
|
72
|
+
expect_lookup_with_output(0, /value a/)
|
100
73
|
end
|
101
74
|
|
102
|
-
it
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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)
|
110
|
-
|
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
|
-
|
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.
|
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).
|
127
|
-
expect(Facter).to receive(:load_external).
|
128
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
157
|
-
|
158
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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, '
|
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(/
|
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
|
-
|
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
|
-
|
106
|
-
File.write(
|
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" =>
|
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(
|
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
|
data/spec/unit/agent_spec.rb
CHANGED
@@ -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
|
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
|
-
@
|
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
|
-
|
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(@
|
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(@
|
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
|
-
|
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(
|
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
|
-
|
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
|
data/spec/unit/confiner_spec.rb
CHANGED
@@ -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
|
-
@
|
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(
|
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(
|
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(
|
57
|
+
expect(coll).to receive(:summary).and_return("myresult")
|
58
58
|
expect(@object.suitable?(false)).to eq("myresult")
|
59
59
|
end
|
60
60
|
end
|
data/spec/unit/daemon_spec.rb
CHANGED
@@ -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(
|
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')
|