puppet 7.16.0-universal-darwin → 7.19.0-universal-darwin
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +76 -15
- data/ext/systemd/puppet.service +2 -1
- data/lib/puppet/agent.rb +47 -11
- data/lib/puppet/application/agent.rb +3 -13
- data/lib/puppet/application/apply.rb +2 -2
- data/lib/puppet/configurer.rb +1 -1
- data/lib/puppet/defaults.rb +11 -1
- data/lib/puppet/face/help.rb +1 -1
- data/lib/puppet/face/module/list.rb +16 -7
- data/lib/puppet/functions/capitalize.rb +1 -1
- data/lib/puppet/generate/type.rb +1 -1
- data/lib/puppet/http/client.rb +22 -2
- data/lib/puppet/info_service/task_information_service.rb +1 -1
- data/lib/puppet/module/task.rb +5 -1
- data/lib/puppet/parameter.rb +19 -4
- data/lib/puppet/parser/templatewrapper.rb +1 -1
- 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/reference/configuration.rb +2 -0
- data/lib/puppet/ssl/ssl_provider.rb +65 -12
- data/lib/puppet/ssl/state_machine.rb +13 -17
- data/lib/puppet/transaction.rb +22 -0
- data/lib/puppet/type/tidy.rb +1 -1
- data/lib/puppet/type/user.rb +3 -0
- data/lib/puppet/type.rb +20 -3
- data/lib/puppet/util/json.rb +5 -2
- data/lib/puppet/util/resource_template.rb +1 -1
- data/lib/puppet/util/selinux.rb +1 -1
- data/lib/puppet/util.rb +12 -1
- data/lib/puppet/version.rb +1 -1
- data/lib/puppet.rb +1 -14
- data/man/man5/puppet.conf.5 +12 -4
- 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/http/client_spec.rb +27 -10
- data/spec/lib/puppet_spec/https.rb +1 -1
- data/spec/lib/puppet_spec/puppetserver.rb +39 -2
- data/spec/unit/agent_spec.rb +28 -2
- data/spec/unit/application/agent_spec.rb +26 -16
- data/spec/unit/daemon_spec.rb +2 -11
- data/spec/unit/face/module/list_spec.rb +26 -0
- data/spec/unit/http/client_spec.rb +18 -0
- data/spec/unit/info_service_spec.rb +11 -3
- 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/task_spec.rb +56 -13
- data/spec/unit/util/resource_template_spec.rb +1 -1
- data/spec/unit/util/selinux_spec.rb +5 -0
- data/spec/unit/util_spec.rb +11 -1
- data/tasks/generate_cert_fixtures.rake +5 -4
- metadata +2 -2
@@ -77,13 +77,13 @@ describe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do
|
|
77
77
|
}
|
78
78
|
}
|
79
79
|
|
80
|
-
let(:
|
81
|
-
res = tmpfile('
|
80
|
+
let(:cert_file) do
|
81
|
+
res = tmpfile('cert_file')
|
82
82
|
File.write(res, https_server.ca_cert)
|
83
83
|
res
|
84
84
|
end
|
85
85
|
|
86
|
-
it "mutually authenticates the connection" do
|
86
|
+
it "mutually authenticates the connection using an explicit context" do
|
87
87
|
client_context = ssl_provider.create_context(
|
88
88
|
cacerts: [https_server.ca_cert], crls: [https_server.ca_crl],
|
89
89
|
client_cert: https_server.server_cert, private_key: https_server.server_key
|
@@ -95,10 +95,27 @@ describe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do
|
|
95
95
|
end
|
96
96
|
end
|
97
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
|
+
|
98
115
|
it "connects when the server's CA is in the system store and the connection is mutually authenticated using create_context" do
|
99
|
-
Puppet::Util.withenv("SSL_CERT_FILE" =>
|
116
|
+
Puppet::Util.withenv("SSL_CERT_FILE" => cert_file) do
|
100
117
|
client_context = ssl_provider.create_context(
|
101
|
-
cacerts: [
|
118
|
+
cacerts: [], crls: [],
|
102
119
|
client_cert: https_server.server_cert, private_key: https_server.server_key,
|
103
120
|
revocation: false, include_system_store: true
|
104
121
|
)
|
@@ -109,8 +126,8 @@ describe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do
|
|
109
126
|
end
|
110
127
|
end
|
111
128
|
|
112
|
-
it "connects when the server's CA is in the system store and the connection is mutually authenticated
|
113
|
-
Puppet::Util.withenv("SSL_CERT_FILE" =>
|
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
|
114
131
|
client_context = ssl_provider.load_context(revocation: false, include_system_store: true)
|
115
132
|
https_server.start_server(ctx_proc: ctx_proc) do |port|
|
116
133
|
res = client.get(URI("https://127.0.0.1:#{port}"), options: {ssl_context: client_context})
|
@@ -132,12 +149,12 @@ describe Puppet::HTTP::Client, unless: Puppet::Util::Platform.jruby? do
|
|
132
149
|
|
133
150
|
it "connects when the server's CA is in the system store" do
|
134
151
|
# create a temp cacert bundle
|
135
|
-
|
136
|
-
File.write(
|
152
|
+
cert_file = tmpfile('cert_file')
|
153
|
+
File.write(cert_file, https_server.ca_cert)
|
137
154
|
|
138
155
|
# override path to system cacert bundle, this must be done before
|
139
156
|
# the SSLContext is created and the call to X509::Store.set_default_paths
|
140
|
-
Puppet::Util.withenv("SSL_CERT_FILE" =>
|
157
|
+
Puppet::Util.withenv("SSL_CERT_FILE" => cert_file) do
|
141
158
|
system_context = ssl_provider.create_system_context(cacerts: [])
|
142
159
|
https_server.start_server do |port|
|
143
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
|
@@ -97,6 +101,8 @@ describe Puppet::Agent do
|
|
97
101
|
end
|
98
102
|
|
99
103
|
it "should splay" do
|
104
|
+
Puppet[:splay] = true
|
105
|
+
|
100
106
|
expect(@agent).to receive(:splay)
|
101
107
|
|
102
108
|
@agent.run
|
@@ -179,6 +185,26 @@ describe Puppet::Agent do
|
|
179
185
|
expect(@agent.run).to eq(:result)
|
180
186
|
end
|
181
187
|
|
188
|
+
it "should check if it's disabled after splaying and log a message" do
|
189
|
+
Puppet[:splay] = true
|
190
|
+
Puppet[:splaylimit] = '5s'
|
191
|
+
Puppet[:onetime] = true
|
192
|
+
|
193
|
+
expect(@agent).to receive(:disabled?).and_return(false, true)
|
194
|
+
|
195
|
+
allow(Puppet).to receive(:notice).and_call_original
|
196
|
+
expect(Puppet).to receive(:notice).with(/Skipping run of .*; administratively disabled.*/)
|
197
|
+
@agent.run
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should check if it's disabled after acquiring the lock and log a message" do
|
201
|
+
expect(@agent).to receive(:disabled?).and_return(false, true)
|
202
|
+
|
203
|
+
allow(Puppet).to receive(:notice).and_call_original
|
204
|
+
expect(Puppet).to receive(:notice).with(/Skipping run of .*; administratively disabled.*/)
|
205
|
+
@agent.run
|
206
|
+
end
|
207
|
+
|
182
208
|
describe "and a puppet agent is already running" do
|
183
209
|
before(:each) do
|
184
210
|
allow_any_instance_of(Object).to receive(:sleep)
|
@@ -195,7 +221,7 @@ describe Puppet::Agent do
|
|
195
221
|
@agent.run
|
196
222
|
end
|
197
223
|
|
198
|
-
it "should inform that a run is already in
|
224
|
+
it "should inform that a run is already in progress and try to run every X seconds if waitforlock is used" do
|
199
225
|
# so the locked file exists
|
200
226
|
allow(File).to receive(:file?).and_return(true)
|
201
227
|
# so we don't have to wait again for the run to exit (default maxwaitforcert is 60)
|
@@ -224,7 +250,7 @@ describe Puppet::Agent do
|
|
224
250
|
@agent.run
|
225
251
|
end
|
226
252
|
end
|
227
|
-
|
253
|
+
|
228
254
|
describe "when should_fork is true", :if => Puppet.features.posix? && RUBY_PLATFORM != 'java' do
|
229
255
|
before do
|
230
256
|
@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
|
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')
|
@@ -227,4 +227,30 @@ describe "puppet module list" do
|
|
227
227
|
console_output(:tree => true)
|
228
228
|
end
|
229
229
|
end
|
230
|
+
|
231
|
+
describe "when rendering as json" do
|
232
|
+
let(:face) { Puppet::Face[:module, :current] }
|
233
|
+
let(:action) { face.get_action(:list) }
|
234
|
+
|
235
|
+
it "should warn about missing dependencies" do
|
236
|
+
PuppetSpec::Modules.create('depender', @modpath1, :metadata => {
|
237
|
+
:version => '1.0.0',
|
238
|
+
:dependencies => [{
|
239
|
+
"version_requirement" => ">= 0.0.5",
|
240
|
+
"name" => "puppetlabs/dependable"
|
241
|
+
}]
|
242
|
+
})
|
243
|
+
|
244
|
+
result = face.list
|
245
|
+
expect(result.dig(:unmet_dependencies, :missing)).to include(
|
246
|
+
"puppetlabs/dependable" => {
|
247
|
+
errors: ["'puppetlabs-depender' (v1.0.0) requires 'puppetlabs-dependable' (>= 0.0.5)"],
|
248
|
+
parent: {
|
249
|
+
name: "puppetlabs/depender", :version=>"v1.0.0"
|
250
|
+
},
|
251
|
+
version: nil
|
252
|
+
}
|
253
|
+
)
|
254
|
+
end
|
255
|
+
end
|
230
256
|
end
|
@@ -120,6 +120,24 @@ describe Puppet::HTTP::Client do
|
|
120
120
|
|
121
121
|
client.close
|
122
122
|
end
|
123
|
+
|
124
|
+
it 'reloads the default ssl context' do
|
125
|
+
expect(client.pool).to receive(:with_connection) do |_, verifier|
|
126
|
+
expect(verifier.ssl_context).to_not equal(puppet_context)
|
127
|
+
end
|
128
|
+
|
129
|
+
client.close
|
130
|
+
client.connect(uri)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'reloads the default system ssl context' do
|
134
|
+
expect(client.pool).to receive(:with_connection) do |_, verifier|
|
135
|
+
expect(verifier.ssl_context).to_not equal(system_context)
|
136
|
+
end
|
137
|
+
|
138
|
+
client.close
|
139
|
+
client.connect(uri, options: {include_system_store: true})
|
140
|
+
end
|
123
141
|
end
|
124
142
|
|
125
143
|
context "for GET requests" do
|
@@ -11,6 +11,9 @@ describe "Puppet::InfoService" do
|
|
11
11
|
|
12
12
|
context 'task information service' do
|
13
13
|
let(:mod_name) { 'test1' }
|
14
|
+
let(:metadata) {
|
15
|
+
{ "private" => true,
|
16
|
+
"description" => "a task that does a thing" } }
|
14
17
|
let(:task_name) { "#{mod_name}::thingtask" }
|
15
18
|
let(:modpath) { tmpdir('modpath') }
|
16
19
|
let(:env_name) { 'testing' }
|
@@ -20,8 +23,13 @@ describe "Puppet::InfoService" do
|
|
20
23
|
context 'tasks_per_environment method' do
|
21
24
|
it "returns task data for the tasks in an environment" do
|
22
25
|
Puppet.override(:environments => env_loader) do
|
23
|
-
PuppetSpec::Modules.create(mod_name, modpath, {:environment => env,
|
24
|
-
|
26
|
+
PuppetSpec::Modules.create(mod_name, modpath, {:environment => env,
|
27
|
+
:tasks => [['thingtask',
|
28
|
+
{:name => 'thingtask.json',
|
29
|
+
:content => metadata.to_json}]]})
|
30
|
+
expect(Puppet::InfoService.tasks_per_environment(env_name)).to eq([{:name => task_name,
|
31
|
+
:module => {:name => mod_name},
|
32
|
+
:metadata => metadata} ])
|
25
33
|
end
|
26
34
|
end
|
27
35
|
|
@@ -207,7 +215,7 @@ describe "Puppet::InfoService" do
|
|
207
215
|
end
|
208
216
|
end
|
209
217
|
end
|
210
|
-
|
218
|
+
|
211
219
|
context 'plan information service' do
|
212
220
|
let(:mod_name) { 'test1' }
|
213
221
|
let(:plan_name) { "#{mod_name}::thingplan" }
|
@@ -17,4 +17,30 @@ describe Puppet::Pops::Evaluator::DeferredResolver do
|
|
17
17
|
|
18
18
|
expect(catalog.resource(:notify, 'deferred')[:message]).to eq('1:2:3')
|
19
19
|
end
|
20
|
+
|
21
|
+
it 'lazily resolves deferred values in a catalog' do
|
22
|
+
catalog = compile_to_catalog(<<~END)
|
23
|
+
notify { "deferred":
|
24
|
+
message => Deferred("join", [[1,2,3], ":"])
|
25
|
+
}
|
26
|
+
END
|
27
|
+
described_class.resolve_and_replace(facts, catalog, environment, false)
|
28
|
+
|
29
|
+
deferred = catalog.resource(:notify, 'deferred')[:message]
|
30
|
+
expect(deferred.resolve).to eq('1:2:3')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'lazily resolves nested deferred values in a catalog' do
|
34
|
+
catalog = compile_to_catalog(<<~END)
|
35
|
+
$args = Deferred("inline_epp", ["<%= 'a,b,c' %>"])
|
36
|
+
notify { "deferred":
|
37
|
+
message => Deferred("split", [$args, ","])
|
38
|
+
}
|
39
|
+
END
|
40
|
+
described_class.resolve_and_replace(facts, catalog, environment, false)
|
41
|
+
|
42
|
+
deferred = catalog.resource(:notify, 'deferred')[:message]
|
43
|
+
expect(deferred.resolve).to eq(["a", "b", "c"])
|
44
|
+
end
|
45
|
+
|
20
46
|
end
|
@@ -606,7 +606,7 @@ describe 'loaders' do
|
|
606
606
|
it "an illegal function is loaded" do
|
607
607
|
expect {
|
608
608
|
loader.load_typed(typed_name(:function, 'bad_func_load3')).value
|
609
|
-
}.to raise_error(SecurityError, /Illegal method definition of method 'bad_func_load3_illegal_method' on line 8 in legacy function/)
|
609
|
+
}.to raise_error(SecurityError, /Illegal method definition of method 'bad_func_load3_illegal_method' in source .*bad_func_load3.rb on line 8 in legacy function/)
|
610
610
|
end
|
611
611
|
end
|
612
612
|
|
@@ -1,12 +1,178 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'puppet/pops'
|
3
3
|
require 'puppet_spec/compiler'
|
4
|
+
require 'puppet_spec/files'
|
5
|
+
require 'puppet/loaders'
|
4
6
|
|
5
7
|
module Puppet::Pops
|
6
8
|
module Types
|
7
9
|
|
8
10
|
describe 'the type mismatch describer' do
|
9
|
-
include PuppetSpec::Compiler
|
11
|
+
include PuppetSpec::Compiler, PuppetSpec::Files
|
12
|
+
|
13
|
+
context 'with deferred functions' do
|
14
|
+
let(:env_name) { 'spec' }
|
15
|
+
let(:code_dir) { Puppet[:environmentpath] }
|
16
|
+
let(:env_dir) { File.join(code_dir, env_name) }
|
17
|
+
let(:env) { Puppet::Node::Environment.create(env_name.to_sym, [File.join(populated_code_dir, env_name, 'modules')]) }
|
18
|
+
let(:node) { Puppet::Node.new('fooname', environment: env) }
|
19
|
+
let(:populated_code_dir) do
|
20
|
+
dir_contained_in(code_dir, env_name => env_content)
|
21
|
+
PuppetSpec::Files.record_tmp(env_dir)
|
22
|
+
code_dir
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:env_content) {
|
26
|
+
{
|
27
|
+
'lib' => {
|
28
|
+
'puppet' => {
|
29
|
+
'functions' => {
|
30
|
+
'string_return.rb' => <<-RUBY.unindent,
|
31
|
+
Puppet::Functions.create_function(:string_return) do
|
32
|
+
dispatch :string_return do
|
33
|
+
param 'String', :arg1
|
34
|
+
return_type 'String'
|
35
|
+
end
|
36
|
+
def string_return(arg1)
|
37
|
+
arg1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
RUBY
|
41
|
+
'variant_return.rb' => <<-RUBY.unindent,
|
42
|
+
Puppet::Functions.create_function(:variant_return) do
|
43
|
+
dispatch :variant_return do
|
44
|
+
param 'String', :arg1
|
45
|
+
return_type 'Variant[Integer,Float]'
|
46
|
+
end
|
47
|
+
def variant_return(arg1)
|
48
|
+
arg1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
RUBY
|
52
|
+
'no_return.rb' => <<-RUBY.unindent,
|
53
|
+
Puppet::Functions.create_function(:no_return) do
|
54
|
+
dispatch :no_return do
|
55
|
+
param 'String', :arg1
|
56
|
+
end
|
57
|
+
def variant_return(arg1)
|
58
|
+
arg1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
RUBY
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
before(:each) do
|
69
|
+
Puppet.push_context(:loaders => Puppet::Pops::Loaders.new(env))
|
70
|
+
end
|
71
|
+
|
72
|
+
after(:each) do
|
73
|
+
Puppet.pop_context
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'will compile when the parameter type matches the function return_type' do
|
77
|
+
code = <<-CODE
|
78
|
+
$d = Deferred("string_return", ['/a/non/existing/path'])
|
79
|
+
class testclass(String $classparam) {
|
80
|
+
}
|
81
|
+
class { 'testclass':
|
82
|
+
classparam => $d
|
83
|
+
}
|
84
|
+
CODE
|
85
|
+
expect { eval_and_collect_notices(code, node) }.to_not raise_error
|
86
|
+
end
|
87
|
+
|
88
|
+
it "will compile when a Variant parameter's types matches the return type" do
|
89
|
+
code = <<-CODE
|
90
|
+
$d = Deferred("string_return", ['/a/non/existing/path'])
|
91
|
+
class testclass(Variant[String, Float] $classparam) {
|
92
|
+
}
|
93
|
+
class { 'testclass':
|
94
|
+
classparam => $d
|
95
|
+
}
|
96
|
+
CODE
|
97
|
+
expect { eval_and_collect_notices(code, node) }.to_not raise_error
|
98
|
+
end
|
99
|
+
|
100
|
+
it "will compile with a union of a Variant parameters' types and Variant return types" do
|
101
|
+
code = <<-CODE
|
102
|
+
$d = Deferred("variant_return", ['/a/non/existing/path'])
|
103
|
+
class testclass(Variant[Any,Float] $classparam) {
|
104
|
+
}
|
105
|
+
class { 'testclass':
|
106
|
+
classparam => $d
|
107
|
+
}
|
108
|
+
CODE
|
109
|
+
expect { eval_and_collect_notices(code, node) }.to_not raise_error
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'will warn when there is no defined return_type for the function definition' do
|
113
|
+
code = <<-CODE
|
114
|
+
$d = Deferred("no_return", ['/a/non/existing/path'])
|
115
|
+
class testclass(Variant[String,Boolean] $classparam) {
|
116
|
+
}
|
117
|
+
class { 'testclass':
|
118
|
+
classparam => $d
|
119
|
+
}
|
120
|
+
CODE
|
121
|
+
expect(Puppet).to receive(:warn_once).with(anything, anything, /.+function no_return has no return_type/).at_least(:once)
|
122
|
+
expect { eval_and_collect_notices(code, node) }.to_not raise_error
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'will report a mismatch between a deferred function return type and class parameter value' do
|
126
|
+
code = <<-CODE
|
127
|
+
$d = Deferred("string_return", ['/a/non/existing/path'])
|
128
|
+
class testclass(Integer $classparam) {
|
129
|
+
}
|
130
|
+
class { 'testclass':
|
131
|
+
classparam => $d
|
132
|
+
}
|
133
|
+
CODE
|
134
|
+
expect { eval_and_collect_notices(code, node) }.to raise_error(Puppet::Error, /.+'classparam' expects an Integer value, got String/)
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'will report an argument error when no matching arity is found' do
|
138
|
+
code = <<-CODE
|
139
|
+
$d = Deferred("string_return", ['/a/non/existing/path', 'second-invalid-arg'])
|
140
|
+
class testclass(Integer $classparam) {
|
141
|
+
}
|
142
|
+
class { 'testclass':
|
143
|
+
classparam => $d
|
144
|
+
}
|
145
|
+
CODE
|
146
|
+
expect { eval_and_collect_notices(code,node) }.to raise_error(Puppet::Error, /.+ No matching arity found for string_return/)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'will error with no matching Variant class parameters and return_type' do
|
150
|
+
code = <<-CODE
|
151
|
+
$d = Deferred("string_return", ['/a/non/existing/path'])
|
152
|
+
class testclass(Variant[Integer,Float] $classparam) {
|
153
|
+
}
|
154
|
+
class { 'testclass':
|
155
|
+
classparam => $d
|
156
|
+
}
|
157
|
+
CODE
|
158
|
+
expect { eval_and_collect_notices(code,node) }.to raise_error(Puppet::Error, /.+'classparam' expects a value of type Integer or Float, got String/)
|
159
|
+
end
|
160
|
+
|
161
|
+
# This test exposes a shortcoming in the #message function for Puppet::Pops::Type::TypeMismatch
|
162
|
+
# where the `actual` is not introspected for the list of Variant types, so the error message
|
163
|
+
# shows that the list of expected types does not match Variant, instead of a list of actual types.
|
164
|
+
it 'will error with no matching Variant class parameters and Variant return_type' do
|
165
|
+
code = <<-CODE
|
166
|
+
$d = Deferred("variant_return", ['/a/non/existing/path'])
|
167
|
+
class testclass(Variant[String,Boolean] $classparam) {
|
168
|
+
}
|
169
|
+
class { 'testclass':
|
170
|
+
classparam => $d
|
171
|
+
}
|
172
|
+
CODE
|
173
|
+
expect { eval_and_collect_notices(code, node) }.to raise_error(Puppet::Error, /.+'classparam' expects a value of type String or Boolean, got Variant/)
|
174
|
+
end
|
175
|
+
end
|
10
176
|
|
11
177
|
it 'will report a mismatch between a hash and a struct with details' do
|
12
178
|
code = <<-CODE
|