puppet 6.13.0 → 6.14.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puppet might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +7 -13
- data/Gemfile.lock +6 -6
- data/README.md +15 -22
- data/lib/puppet.rb +1 -1
- data/lib/puppet/application/agent.rb +9 -11
- data/lib/puppet/application/describe.rb +7 -5
- data/lib/puppet/application/device.rb +2 -2
- data/lib/puppet/application/filebucket.rb +14 -1
- data/lib/puppet/application/ssl.rb +1 -1
- data/lib/puppet/configurer.rb +30 -41
- data/lib/puppet/configurer/plugin_handler.rb +10 -1
- data/lib/puppet/defaults.rb +7 -1
- data/lib/puppet/face/plugin.rb +1 -1
- data/lib/puppet/functions/eyaml_lookup_key.rb +13 -8
- data/lib/puppet/http.rb +1 -0
- data/lib/puppet/http/client.rb +69 -34
- data/lib/puppet/http/resolver/server_list.rb +2 -2
- data/lib/puppet/http/resolver/settings.rb +1 -1
- data/lib/puppet/http/resolver/srv.rb +1 -1
- data/lib/puppet/http/response.rb +6 -1
- data/lib/puppet/http/service.rb +30 -11
- data/lib/puppet/http/service/ca.rb +8 -8
- data/lib/puppet/http/service/compiler.rb +41 -10
- data/lib/puppet/http/service/file_server.rb +40 -20
- data/lib/puppet/http/service/report.rb +12 -15
- data/lib/puppet/http/session.rb +39 -1
- data/lib/puppet/indirector/catalog/rest.rb +33 -0
- data/lib/puppet/indirector/facts/rest.rb +41 -0
- data/lib/puppet/indirector/file_content/rest.rb +30 -0
- data/lib/puppet/indirector/file_metadata/rest.rb +50 -0
- data/lib/puppet/indirector/node/rest.rb +23 -0
- data/lib/puppet/indirector/report/rest.rb +19 -0
- data/lib/puppet/indirector/rest.rb +6 -0
- data/lib/puppet/indirector/status/rest.rb +17 -0
- data/lib/puppet/loaders.rb +6 -0
- data/lib/puppet/network/http/base_pool.rb +1 -1
- data/lib/puppet/network/http/pool.rb +6 -1
- data/lib/puppet/provider/group/groupadd.rb +9 -4
- data/lib/puppet/runtime.rb +8 -1
- data/lib/puppet/settings.rb +2 -0
- data/lib/puppet/settings/http_extra_headers_setting.rb +25 -0
- data/lib/puppet/ssl/state_machine.rb +4 -0
- data/lib/puppet/test/test_helper.rb +3 -1
- data/lib/puppet/type/file.rb +13 -0
- data/lib/puppet/type/file/source.rb +47 -58
- data/lib/puppet/version.rb +1 -1
- data/locales/puppet.pot +167 -160
- data/man/man5/puppet.conf.5 +11 -3
- data/man/man8/puppet-agent.8 +6 -6
- 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 +2 -2
- 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 +17 -2
- data/man/man8/puppet-generate.8 +1 -1
- data/man/man8/puppet-help.8 +1 -1
- data/man/man8/puppet-key.8 +1 -1
- data/man/man8/puppet-lookup.8 +1 -1
- data/man/man8/puppet-man.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 +2 -2
- data/man/man8/puppet-status.8 +1 -1
- data/man/man8/puppet.8 +2 -2
- data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_md5/should_fetch_if_not_on_the_local_disk.yml +1 -67
- data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_md5/should_not_update_if_content_on_disk_is_up-to-date.yml +1 -69
- data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_md5/should_update_if_content_differs_on_disk.yml +1 -69
- data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_fetch_if_mtime_is_older_on_disk.yml +1 -67
- data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_fetch_if_no_header_specified.yml +1 -65
- data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_fetch_if_not_on_the_local_disk.yml +1 -67
- data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_not_update_if_mtime_is_newer_on_disk.yml +1 -67
- data/spec/integration/faces/plugin_spec.rb +3 -1
- data/spec/integration/http/client_spec.rb +11 -0
- data/spec/integration/network/http_pool_spec.rb +9 -1
- data/spec/unit/application/describe_spec.rb +88 -50
- data/spec/unit/configurer/plugin_handler_spec.rb +36 -19
- data/spec/unit/configurer_spec.rb +16 -14
- data/spec/unit/face/plugin_spec.rb +12 -10
- data/spec/unit/functions/lookup_spec.rb +13 -0
- data/spec/unit/http/client_spec.rb +172 -1
- data/spec/unit/http/resolver_spec.rb +14 -2
- data/spec/unit/http/response_spec.rb +69 -0
- data/spec/unit/http/service/ca_spec.rb +28 -9
- data/spec/unit/http/service/compiler_spec.rb +151 -24
- data/spec/unit/http/service/file_server_spec.rb +65 -8
- data/spec/unit/http/service/report_spec.rb +17 -8
- data/spec/unit/http/service_spec.rb +92 -3
- data/spec/unit/http/session_spec.rb +104 -1
- data/spec/unit/indirector/catalog/rest_spec.rb +59 -2
- data/spec/unit/indirector/facts/rest_spec.rb +79 -24
- data/spec/unit/indirector/file_content/rest_spec.rb +53 -2
- data/spec/unit/indirector/file_metadata/rest_spec.rb +109 -2
- data/spec/unit/indirector/node/rest_spec.rb +57 -2
- data/spec/unit/indirector/report/rest_spec.rb +58 -51
- data/spec/unit/indirector/resource/ral_spec.rb +7 -8
- data/spec/unit/indirector/status/rest_spec.rb +43 -2
- data/spec/unit/network/http/pool_spec.rb +57 -11
- data/spec/unit/provider/group/groupadd_spec.rb +22 -8
- data/spec/unit/settings/autosign_setting_spec.rb +1 -1
- data/spec/unit/settings/http_extra_headers_spec.rb +64 -0
- data/spec/unit/ssl/state_machine_spec.rb +10 -0
- data/spec/unit/transaction_spec.rb +0 -2
- data/spec/unit/type/file/ensure_spec.rb +1 -2
- data/spec/unit/type/file/source_spec.rb +86 -35
- data/spec/unit/util/at_fork_spec.rb +1 -0
- data/spec/unit/util/pidlock_spec.rb +36 -24
- metadata +7 -3
- data/COMMITTERS.md +0 -244
@@ -3082,6 +3082,19 @@ describe "The lookup function" do
|
|
3082
3082
|
|
3083
3083
|
let(:env_data) { data_files }
|
3084
3084
|
|
3085
|
+
context 'with unencryptable eyaml' do
|
3086
|
+
let(:data_files) do
|
3087
|
+
{
|
3088
|
+
'common.eyaml' => <<-YAML.unindent
|
3089
|
+
key_with_invalid_eyaml: ENC[PKCS7,INVALID]
|
3090
|
+
YAML
|
3091
|
+
}
|
3092
|
+
end
|
3093
|
+
|
3094
|
+
it 'fails and reports error with ENC value, key being looked up and filename' do
|
3095
|
+
expect { lookup('key_with_invalid_eyaml') }.to raise_error(Puppet::DataBinding::LookupError, /hiera-eyaml backend error decrypting ENC\[PKCS7,INVALID\] when looking up key_with_invalid_eyaml in .*common\.eyaml\. Error was/)
|
3096
|
+
end
|
3097
|
+
end
|
3085
3098
|
context 'and a module using eyaml with different options' do
|
3086
3099
|
|
3087
3100
|
let(:private_module_key) do
|
@@ -4,7 +4,10 @@ require 'puppet/http'
|
|
4
4
|
|
5
5
|
describe Puppet::HTTP::Client do
|
6
6
|
let(:uri) { URI.parse('https://www.example.com') }
|
7
|
-
let(:
|
7
|
+
let(:pool) { Puppet::Network::HTTP::Pool.new }
|
8
|
+
let(:puppet_context) { Puppet::SSL::SSLContext.new }
|
9
|
+
let(:system_context) { Puppet::SSL::SSLContext.new }
|
10
|
+
let(:client) { described_class.new(pool: pool, ssl_context: puppet_context, system_ssl_context: system_context) }
|
8
11
|
let(:credentials) { ['user', 'pass'] }
|
9
12
|
|
10
13
|
it 'creates unique sessions' do
|
@@ -45,6 +48,38 @@ describe Puppet::HTTP::Client do
|
|
45
48
|
client.connect(uri)
|
46
49
|
}.to raise_error(Puppet::HTTP::ConnectionError, %r{^Request to https://www.example.com timed out connect operation after .* seconds})
|
47
50
|
end
|
51
|
+
|
52
|
+
it 'connects using the default ssl context' do
|
53
|
+
expect(pool).to receive(:with_connection) do |_, verifier|
|
54
|
+
expect(verifier.ssl_context).to equal(puppet_context)
|
55
|
+
end
|
56
|
+
|
57
|
+
client.connect(uri)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'connects using a specified ssl context' do
|
61
|
+
other_context = Puppet::SSL::SSLContext.new
|
62
|
+
|
63
|
+
expect(pool).to receive(:with_connection) do |_, verifier|
|
64
|
+
expect(verifier.ssl_context).to equal(other_context)
|
65
|
+
end
|
66
|
+
|
67
|
+
client.connect(uri, ssl_context: other_context)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'connects using the system store' do
|
71
|
+
expect(pool).to receive(:with_connection) do |_, verifier|
|
72
|
+
expect(verifier.ssl_context).to equal(system_context)
|
73
|
+
end
|
74
|
+
|
75
|
+
client.connect(uri, include_system_store: true)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'raises an HTTPError if both are specified' do
|
79
|
+
expect {
|
80
|
+
client.connect(uri, ssl_context: puppet_context, include_system_store: true)
|
81
|
+
}.to raise_error(Puppet::HTTP::HTTPError, /The ssl_context and include_system_store parameters are mutually exclusive/)
|
82
|
+
end
|
48
83
|
end
|
49
84
|
|
50
85
|
context 'after connecting' do
|
@@ -138,6 +173,28 @@ describe Puppet::HTTP::Client do
|
|
138
173
|
|
139
174
|
expect(io.string).to eq("abc")
|
140
175
|
end
|
176
|
+
|
177
|
+
context 'when connecting' do
|
178
|
+
it 'uses a specified ssl context' do
|
179
|
+
stub_request(:get, uri).to_return(body: "abc")
|
180
|
+
|
181
|
+
other_context = Puppet::SSL::SSLContext.new
|
182
|
+
|
183
|
+
client.get(uri, ssl_context: other_context)
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'uses the system store' do
|
187
|
+
stub_request(:get, uri).to_return(body: "abc")
|
188
|
+
|
189
|
+
client.get(uri, include_system_store: true)
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'raises an HTTPError if both are specified' do
|
193
|
+
expect {
|
194
|
+
client.get(uri, ssl_context: puppet_context, include_system_store: true)
|
195
|
+
}.to raise_error(Puppet::HTTP::HTTPError, /The ssl_context and include_system_store parameters are mutually exclusive/)
|
196
|
+
end
|
197
|
+
end
|
141
198
|
end
|
142
199
|
|
143
200
|
context "for HEAD requests" do
|
@@ -173,6 +230,28 @@ describe Puppet::HTTP::Client do
|
|
173
230
|
|
174
231
|
expect(client.head(uri).body).to eq("abc")
|
175
232
|
end
|
233
|
+
|
234
|
+
context 'when connecting' do
|
235
|
+
it 'uses a specified ssl context' do
|
236
|
+
stub_request(:head, uri)
|
237
|
+
|
238
|
+
other_context = Puppet::SSL::SSLContext.new
|
239
|
+
|
240
|
+
client.head(uri, ssl_context: other_context)
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'uses the system store' do
|
244
|
+
stub_request(:head, uri)
|
245
|
+
|
246
|
+
client.head(uri, include_system_store: true)
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'raises an HTTPError if both are specified' do
|
250
|
+
expect {
|
251
|
+
client.head(uri, ssl_context: puppet_context, include_system_store: true)
|
252
|
+
}.to raise_error(Puppet::HTTP::HTTPError, /The ssl_context and include_system_store parameters are mutually exclusive/)
|
253
|
+
end
|
254
|
+
end
|
176
255
|
end
|
177
256
|
|
178
257
|
context "for PUT requests" do
|
@@ -211,6 +290,28 @@ describe Puppet::HTTP::Client do
|
|
211
290
|
|
212
291
|
client.put(uri, content_type: 'text/plain', body: "hello")
|
213
292
|
end
|
293
|
+
|
294
|
+
context 'when connecting' do
|
295
|
+
it 'uses a specified ssl context' do
|
296
|
+
stub_request(:put, uri)
|
297
|
+
|
298
|
+
other_context = Puppet::SSL::SSLContext.new
|
299
|
+
|
300
|
+
client.put(uri, content_type: 'text/plain', body: "", ssl_context: other_context)
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'uses the system store' do
|
304
|
+
stub_request(:put, uri)
|
305
|
+
|
306
|
+
client.put(uri, content_type: 'text/plain', body: "", include_system_store: true)
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'raises an HTTPError if both are specified' do
|
310
|
+
expect {
|
311
|
+
client.put(uri, content_type: 'text/plain', body: "", ssl_context: puppet_context, include_system_store: true)
|
312
|
+
}.to raise_error(Puppet::HTTP::HTTPError, /The ssl_context and include_system_store parameters are mutually exclusive/)
|
313
|
+
end
|
314
|
+
end
|
214
315
|
end
|
215
316
|
|
216
317
|
context "for POST requests" do
|
@@ -259,6 +360,28 @@ describe Puppet::HTTP::Client do
|
|
259
360
|
|
260
361
|
expect(io.string).to eq("abc")
|
261
362
|
end
|
363
|
+
|
364
|
+
context 'when connecting' do
|
365
|
+
it 'uses a specified ssl context' do
|
366
|
+
stub_request(:post, uri)
|
367
|
+
|
368
|
+
other_context = Puppet::SSL::SSLContext.new
|
369
|
+
|
370
|
+
client.post(uri, content_type: 'text/plain', body: "", ssl_context: other_context)
|
371
|
+
end
|
372
|
+
|
373
|
+
it 'uses the system store' do
|
374
|
+
stub_request(:post, uri)
|
375
|
+
|
376
|
+
client.post(uri, content_type: 'text/plain', body: "", include_system_store: true)
|
377
|
+
end
|
378
|
+
|
379
|
+
it 'raises an HTTPError if both are specified' do
|
380
|
+
expect {
|
381
|
+
client.post(uri, content_type: 'text/plain', body: "", ssl_context: puppet_context, include_system_store: true)
|
382
|
+
}.to raise_error(Puppet::HTTP::HTTPError, /The ssl_context and include_system_store parameters are mutually exclusive/)
|
383
|
+
end
|
384
|
+
end
|
262
385
|
end
|
263
386
|
|
264
387
|
context "for DELETE requests" do
|
@@ -294,6 +417,28 @@ describe Puppet::HTTP::Client do
|
|
294
417
|
|
295
418
|
expect(client.delete(uri).body).to eq("abc")
|
296
419
|
end
|
420
|
+
|
421
|
+
context 'when connecting' do
|
422
|
+
it 'uses a specified ssl context' do
|
423
|
+
stub_request(:delete, uri)
|
424
|
+
|
425
|
+
other_context = Puppet::SSL::SSLContext.new
|
426
|
+
|
427
|
+
client.delete(uri, ssl_context: other_context)
|
428
|
+
end
|
429
|
+
|
430
|
+
it 'uses the system store' do
|
431
|
+
stub_request(:delete, uri)
|
432
|
+
|
433
|
+
client.delete(uri, include_system_store: true)
|
434
|
+
end
|
435
|
+
|
436
|
+
it 'raises an HTTPError if both are specified' do
|
437
|
+
expect {
|
438
|
+
client.delete(uri, ssl_context: puppet_context, include_system_store: true)
|
439
|
+
}.to raise_error(Puppet::HTTP::HTTPError, /The ssl_context and include_system_store parameters are mutually exclusive/)
|
440
|
+
end
|
441
|
+
end
|
297
442
|
end
|
298
443
|
|
299
444
|
context "Basic Auth" do
|
@@ -539,6 +684,32 @@ describe Puppet::HTTP::Client do
|
|
539
684
|
}.to raise_error(Puppet::HTTP::ProtocolError, /Failed to parse Retry-After header 'foo' as an integer or RFC 2822 date/)
|
540
685
|
end
|
541
686
|
|
687
|
+
it "should close the connection before sleeping" do
|
688
|
+
retry_after('42')
|
689
|
+
|
690
|
+
site = Puppet::Network::HTTP::Site.from_uri(uri)
|
691
|
+
|
692
|
+
http1 = Net::HTTP.new(site.host, site.port)
|
693
|
+
http1.use_ssl = true
|
694
|
+
allow(http1).to receive(:started?).and_return(true)
|
695
|
+
|
696
|
+
http2 = Net::HTTP.new(site.host, site.port)
|
697
|
+
http2.use_ssl = true
|
698
|
+
allow(http2).to receive(:started?).and_return(true)
|
699
|
+
|
700
|
+
|
701
|
+
pool = Puppet::Network::HTTP::Pool.new()
|
702
|
+
client = Puppet::HTTP::Client.new(pool: pool)
|
703
|
+
|
704
|
+
# The "with_connection" method is required to yield started connections
|
705
|
+
allow(pool).to receive(:with_connection).and_yield(http1).and_yield(http2)
|
706
|
+
|
707
|
+
expect(http1).to receive(:finish).ordered
|
708
|
+
expect(::Kernel).to receive(:sleep).with(42).ordered
|
709
|
+
|
710
|
+
client.get(uri)
|
711
|
+
end
|
712
|
+
|
542
713
|
it "should sleep and retry if Retry-After is an Integer" do
|
543
714
|
retry_after('42')
|
544
715
|
|
@@ -22,10 +22,11 @@ describe Puppet::HTTP::Resolver do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
context 'when resolving using server_list' do
|
25
|
+
let(:subject) { Puppet::HTTP::Resolver::ServerList.new(client, server_list_setting: Puppet.settings.setting(:server_list), default_port: 8142, services: Puppet::HTTP::Service::SERVICE_NAMES) }
|
26
|
+
|
25
27
|
before :each do
|
26
28
|
Puppet[:server_list] = 'ca.example.com:8141,apple.example.com'
|
27
29
|
end
|
28
|
-
let(:subject) { Puppet::HTTP::Resolver::ServerList.new(client, server_list_setting: Puppet.settings.setting(:server_list), default_port: 8142, services: [:puppet, :ca]) }
|
29
30
|
|
30
31
|
it 'returns a service based on the current server_list setting' do
|
31
32
|
stub_request(:get, "https://ca.example.com:8141/status/v1/simple/master").to_return(status: 200)
|
@@ -43,7 +44,18 @@ describe Puppet::HTTP::Resolver do
|
|
43
44
|
expect(service.url.to_s).to eq("https://ca.example.com:8141/puppet-ca/v1")
|
44
45
|
end
|
45
46
|
|
46
|
-
it '
|
47
|
+
it 'logs unsuccessful HTTP 500 responses' do
|
48
|
+
Puppet[:log_level] = "debug"
|
49
|
+
|
50
|
+
stub_request(:get, "https://ca.example.com:8141/status/v1/simple/master").to_return(status: [500, 'Internal Server Error'])
|
51
|
+
stub_request(:get, "https://apple.example.com:8142/status/v1/simple/master").to_return(status: 200)
|
52
|
+
|
53
|
+
subject.resolve(session, :ca)
|
54
|
+
|
55
|
+
expect(@logs.map(&:message)).to include(/Puppet server ca.example.com:8141 is unavailable: 500 Internal Server Error/)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'fails if no servers in server_list are accessible' do
|
47
59
|
stub_request(:get, "https://ca.example.com:8141/status/v1/simple/master").to_return(status: 503)
|
48
60
|
stub_request(:get, "https://apple.example.com:8142/status/v1/simple/master").to_return(status: 503)
|
49
61
|
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'puppet/http'
|
3
|
+
|
4
|
+
describe Puppet::HTTP::Response do
|
5
|
+
let(:uri) { URI.parse('https://www.example.com') }
|
6
|
+
let(:client) { Puppet::HTTP::Client.new }
|
7
|
+
|
8
|
+
it "returns the request URL" do
|
9
|
+
stub_request(:get, uri)
|
10
|
+
|
11
|
+
response = client.get(uri)
|
12
|
+
expect(response.url).to eq(uri)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns the HTTP code" do
|
16
|
+
stub_request(:get, uri)
|
17
|
+
|
18
|
+
response = client.get(uri)
|
19
|
+
expect(response.code).to eq(200)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "returns the HTTP reason string" do
|
23
|
+
stub_request(:get, uri).to_return(status: [418, "I'm a teapot"])
|
24
|
+
|
25
|
+
response = client.get(uri)
|
26
|
+
expect(response.reason).to eq("I'm a teapot")
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns the response body" do
|
30
|
+
stub_request(:get, uri).to_return(status: 200, body: "I'm the body")
|
31
|
+
|
32
|
+
response = client.get(uri)
|
33
|
+
expect(response.body).to eq("I'm the body")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "streams the response body" do
|
37
|
+
stub_request(:get, uri).to_return(status: 200, body: "I'm the streaming body")
|
38
|
+
|
39
|
+
content = StringIO.new
|
40
|
+
client.get(uri) do |response|
|
41
|
+
response.read_body do |data|
|
42
|
+
content << data
|
43
|
+
end
|
44
|
+
end
|
45
|
+
expect(content.string).to eq("I'm the streaming body")
|
46
|
+
end
|
47
|
+
|
48
|
+
it "raises if a block isn't given when streaming" do
|
49
|
+
stub_request(:get, uri).to_return(status: 200, body: "")
|
50
|
+
|
51
|
+
expect {
|
52
|
+
client.get(uri) do |response|
|
53
|
+
response.read_body
|
54
|
+
end
|
55
|
+
}.to raise_error(Puppet::HTTP::HTTPError, %r{Request to https://www.example.com failed after .* seconds: A block is required})
|
56
|
+
end
|
57
|
+
|
58
|
+
it "returns success for all 2xx codes" do
|
59
|
+
stub_request(:get, uri).to_return(status: 202)
|
60
|
+
|
61
|
+
expect(client.get(uri)).to be_success
|
62
|
+
end
|
63
|
+
|
64
|
+
it "returns a header value" do
|
65
|
+
stub_request(:get, uri).to_return(status: 200, headers: { 'Content-Encoding' => 'gzip' })
|
66
|
+
|
67
|
+
expect(client.get(uri)['Content-Encoding']).to eq('gzip')
|
68
|
+
end
|
69
|
+
end
|
@@ -5,6 +5,7 @@ require 'puppet/http'
|
|
5
5
|
describe Puppet::HTTP::Service::Ca do
|
6
6
|
let(:ssl_context) { Puppet::SSL::SSLContext.new }
|
7
7
|
let(:client) { Puppet::HTTP::Client.new(ssl_context: ssl_context) }
|
8
|
+
let(:session) { Puppet::HTTP::Session.new(client, []) }
|
8
9
|
let(:subject) { client.create_session.route_to(:ca) }
|
9
10
|
|
10
11
|
before :each do
|
@@ -23,15 +24,6 @@ describe Puppet::HTTP::Service::Ca do
|
|
23
24
|
|
24
25
|
subject.get_certificate('ca')
|
25
26
|
end
|
26
|
-
|
27
|
-
|
28
|
-
it 'includes the X-Puppet-Profiling header when Puppet[:profile] is true' do
|
29
|
-
stub_request(:get, uri).with(headers: {'X-Puppet-Version' => /./, 'User-Agent' => /./, 'X-Puppet-Profiling' => 'true'})
|
30
|
-
|
31
|
-
Puppet[:profile] = true
|
32
|
-
|
33
|
-
subject.get_certificate('ca')
|
34
|
-
end
|
35
27
|
end
|
36
28
|
|
37
29
|
context 'when routing to the CA service' do
|
@@ -64,6 +56,15 @@ describe Puppet::HTTP::Service::Ca do
|
|
64
56
|
let(:pem) { cert.to_pem }
|
65
57
|
let(:url) { "https://www.example.com/puppet-ca/v1/certificate/ca" }
|
66
58
|
|
59
|
+
it 'includes headers set via the :http_extra_headers and :profile settings' do
|
60
|
+
stub_request(:get, url).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'})
|
61
|
+
|
62
|
+
Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'
|
63
|
+
Puppet[:profile] = true
|
64
|
+
|
65
|
+
subject.get_certificate('ca')
|
66
|
+
end
|
67
|
+
|
67
68
|
it 'gets a certificate from the "certificate" endpoint' do
|
68
69
|
stub_request(:get, url).to_return(body: pem)
|
69
70
|
|
@@ -94,6 +95,15 @@ describe Puppet::HTTP::Service::Ca do
|
|
94
95
|
let(:pem) { crl.to_pem }
|
95
96
|
let(:url) { "https://www.example.com/puppet-ca/v1/certificate_revocation_list/ca" }
|
96
97
|
|
98
|
+
it 'includes headers set via the :http_extra_headers and :profile settings' do
|
99
|
+
stub_request(:get, url).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'})
|
100
|
+
|
101
|
+
Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'
|
102
|
+
Puppet[:profile] = true
|
103
|
+
|
104
|
+
subject.get_certificate_revocation_list
|
105
|
+
end
|
106
|
+
|
97
107
|
it 'gets a CRL from "certificate_revocation_list" endpoint' do
|
98
108
|
stub_request(:get, url).to_return(body: pem)
|
99
109
|
|
@@ -136,6 +146,15 @@ describe Puppet::HTTP::Service::Ca do
|
|
136
146
|
let(:pem) { request.to_pem }
|
137
147
|
let(:url) { "https://www.example.com/puppet-ca/v1/certificate_request/infinity" }
|
138
148
|
|
149
|
+
it 'includes headers set via the :http_extra_headers and :profile settings' do
|
150
|
+
stub_request(:put, url).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'})
|
151
|
+
|
152
|
+
Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'
|
153
|
+
Puppet[:profile] = true
|
154
|
+
|
155
|
+
subject.put_certificate_request('infinity', request)
|
156
|
+
end
|
157
|
+
|
139
158
|
it 'submits a CSR to the "certificate_request" endpoint' do
|
140
159
|
stub_request(:put, url).with(body: pem, headers: { 'Content-Type' => 'text/plain' })
|
141
160
|
|
@@ -12,6 +12,7 @@ describe Puppet::HTTP::Service::Compiler do
|
|
12
12
|
let(:node) { Puppet::Node.new(certname) }
|
13
13
|
let(:facts) { Puppet::Node::Facts.new(certname) }
|
14
14
|
let(:catalog) { Puppet::Resource::Catalog.new(certname) }
|
15
|
+
let(:status) { Puppet::Status.new }
|
15
16
|
let(:formatter) { Puppet::Network::FormatHandler.format(:json) }
|
16
17
|
|
17
18
|
before :each do
|
@@ -30,16 +31,7 @@ describe Puppet::HTTP::Service::Compiler do
|
|
30
31
|
expect(request.headers).to_not include('X-Puppet-Profiling')
|
31
32
|
end.to_return(body: formatter.render(catalog), headers: {'Content-Type' => formatter.mime })
|
32
33
|
|
33
|
-
subject.
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'includes the X-Puppet-Profiling header in requests when Puppet[:profile] is true' do
|
37
|
-
stub_request(:post, uri).with(headers: {'X-Puppet-Version' => /./, 'User-Agent' => /./, 'X-Puppet-Profiling' => 'true'})
|
38
|
-
.to_return(body: formatter.render(catalog), headers: {'Content-Type' => formatter.mime })
|
39
|
-
|
40
|
-
Puppet[:profile] = true
|
41
|
-
|
42
|
-
subject.get_catalog(certname, environment: environment, facts: facts)
|
34
|
+
subject.post_catalog(certname, environment: environment, facts: facts)
|
43
35
|
end
|
44
36
|
end
|
45
37
|
|
@@ -51,7 +43,7 @@ describe Puppet::HTTP::Service::Compiler do
|
|
51
43
|
stub_request(:post, "https://compiler2.example.com:8141/puppet/v3/catalog/ziggy?environment=testing")
|
52
44
|
.to_return(body: formatter.render(catalog), headers: {'Content-Type' => formatter.mime })
|
53
45
|
|
54
|
-
subject.
|
46
|
+
subject.post_catalog(certname, environment: environment, facts: facts)
|
55
47
|
end
|
56
48
|
end
|
57
49
|
|
@@ -59,12 +51,22 @@ describe Puppet::HTTP::Service::Compiler do
|
|
59
51
|
let(:uri) { %r{/puppet/v3/catalog/ziggy} }
|
60
52
|
let(:catalog_response) { { body: formatter.render(catalog), headers: {'Content-Type' => formatter.mime } } }
|
61
53
|
|
54
|
+
it 'includes puppet headers set via the :http_extra_headers and :profile settings' do
|
55
|
+
stub_request(:post, uri).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'}).
|
56
|
+
to_return(body: formatter.render(catalog), headers: {'Content-Type' => formatter.mime })
|
57
|
+
|
58
|
+
Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'
|
59
|
+
Puppet[:profile] = true
|
60
|
+
|
61
|
+
subject.post_catalog(certname, environment: environment, facts: facts)
|
62
|
+
end
|
63
|
+
|
62
64
|
it 'submits facts as application/json by default' do
|
63
65
|
stub_request(:post, uri)
|
64
66
|
.with(body: hash_including("facts_format" => /application\/json/))
|
65
67
|
.to_return(**catalog_response)
|
66
68
|
|
67
|
-
subject.
|
69
|
+
subject.post_catalog(certname, environment: environment, facts: facts)
|
68
70
|
end
|
69
71
|
|
70
72
|
it 'submits facts as pson if set as the preferred format' do
|
@@ -74,7 +76,7 @@ describe Puppet::HTTP::Service::Compiler do
|
|
74
76
|
.with(body: hash_including("facts_format" => /pson/))
|
75
77
|
.to_return(**catalog_response)
|
76
78
|
|
77
|
-
subject.
|
79
|
+
subject.post_catalog(certname, environment: environment, facts: facts)
|
78
80
|
end
|
79
81
|
|
80
82
|
it 'includes environment as a query parameter AND in the POST body' do
|
@@ -83,7 +85,7 @@ describe Puppet::HTTP::Service::Compiler do
|
|
83
85
|
body: hash_including("environment" => 'outerspace'))
|
84
86
|
.to_return(**catalog_response)
|
85
87
|
|
86
|
-
subject.
|
88
|
+
subject.post_catalog(certname, environment: 'outerspace', facts: facts)
|
87
89
|
end
|
88
90
|
|
89
91
|
it 'includes configured_environment' do
|
@@ -91,7 +93,7 @@ describe Puppet::HTTP::Service::Compiler do
|
|
91
93
|
.with(body: hash_including("configured_environment" => 'agent_specified'))
|
92
94
|
.to_return(**catalog_response)
|
93
95
|
|
94
|
-
subject.
|
96
|
+
subject.post_catalog(certname, environment: 'production', facts: facts, configured_environment: 'agent_specified')
|
95
97
|
end
|
96
98
|
|
97
99
|
it 'includes transaction_uuid' do
|
@@ -101,7 +103,7 @@ describe Puppet::HTTP::Service::Compiler do
|
|
101
103
|
.with(body: hash_including("transaction_uuid" => uuid))
|
102
104
|
.to_return(**catalog_response)
|
103
105
|
|
104
|
-
subject.
|
106
|
+
subject.post_catalog(certname, environment: 'production', facts: facts, transaction_uuid: uuid)
|
105
107
|
end
|
106
108
|
|
107
109
|
it 'includes job_uuid' do
|
@@ -111,7 +113,7 @@ describe Puppet::HTTP::Service::Compiler do
|
|
111
113
|
.with(body: hash_including("job_uuid" => uuid))
|
112
114
|
.to_return(**catalog_response)
|
113
115
|
|
114
|
-
subject.
|
116
|
+
subject.post_catalog(certname, environment: 'production', facts: facts, job_uuid: uuid)
|
115
117
|
end
|
116
118
|
|
117
119
|
it 'includes static_catalog' do
|
@@ -119,7 +121,7 @@ describe Puppet::HTTP::Service::Compiler do
|
|
119
121
|
.with(body: hash_including("static_catalog" => "false"))
|
120
122
|
.to_return(**catalog_response)
|
121
123
|
|
122
|
-
subject.
|
124
|
+
subject.post_catalog(certname, environment: 'production', facts: facts, static_catalog: false)
|
123
125
|
end
|
124
126
|
|
125
127
|
it 'includes dot-separated list of checksum_types' do
|
@@ -127,14 +129,14 @@ describe Puppet::HTTP::Service::Compiler do
|
|
127
129
|
.with(body: hash_including("checksum_type" => "sha256.sha384"))
|
128
130
|
.to_return(**catalog_response)
|
129
131
|
|
130
|
-
subject.
|
132
|
+
subject.post_catalog(certname, environment: 'production', facts: facts, checksum_type: %w[sha256 sha384])
|
131
133
|
end
|
132
134
|
|
133
135
|
it 'returns a deserialized catalog' do
|
134
136
|
stub_request(:post, uri)
|
135
137
|
.to_return(**catalog_response)
|
136
138
|
|
137
|
-
cat = subject.
|
139
|
+
cat = subject.post_catalog(certname, environment: 'production', facts: facts)
|
138
140
|
expect(cat).to be_a(Puppet::Resource::Catalog)
|
139
141
|
expect(cat.name).to eq(certname)
|
140
142
|
end
|
@@ -144,7 +146,7 @@ describe Puppet::HTTP::Service::Compiler do
|
|
144
146
|
.to_return(status: [500, "Server Error"])
|
145
147
|
|
146
148
|
expect {
|
147
|
-
subject.
|
149
|
+
subject.post_catalog(certname, environment: 'production', facts: facts)
|
148
150
|
}.to raise_error do |err|
|
149
151
|
expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)
|
150
152
|
expect(err.message).to eq('Server Error')
|
@@ -157,7 +159,7 @@ describe Puppet::HTTP::Service::Compiler do
|
|
157
159
|
.to_return(body: "content-type is missing")
|
158
160
|
|
159
161
|
expect {
|
160
|
-
subject.
|
162
|
+
subject.post_catalog(certname, environment: 'production', facts: facts)
|
161
163
|
}.to raise_error(Puppet::HTTP::ProtocolError, /No content type in http response; cannot parse/)
|
162
164
|
end
|
163
165
|
|
@@ -166,7 +168,7 @@ describe Puppet::HTTP::Service::Compiler do
|
|
166
168
|
.to_return(body: "this isn't valid JSON", headers: {'Content-Type' => 'application/json'})
|
167
169
|
|
168
170
|
expect {
|
169
|
-
subject.
|
171
|
+
subject.post_catalog(certname, environment: 'production', facts: facts)
|
170
172
|
}.to raise_error(Puppet::HTTP::SerializationError, /Failed to deserialize Puppet::Resource::Catalog from json/)
|
171
173
|
end
|
172
174
|
|
@@ -194,7 +196,7 @@ describe Puppet::HTTP::Service::Compiler do
|
|
194
196
|
.with(body: hash_including("facts" => /#{test_fact[:encoded]}/))
|
195
197
|
.to_return(**catalog_response)
|
196
198
|
|
197
|
-
subject.
|
199
|
+
subject.post_catalog(certname, environment: environment, facts: facts)
|
198
200
|
end
|
199
201
|
end
|
200
202
|
end
|
@@ -204,6 +206,16 @@ describe Puppet::HTTP::Service::Compiler do
|
|
204
206
|
let(:uri) { %r{/puppet/v3/node/ziggy} }
|
205
207
|
let(:node_response) { { body: formatter.render(node), headers: {'Content-Type' => formatter.mime } } }
|
206
208
|
|
209
|
+
it 'includes custom headers set via the :http_extra_headers and :profile settings' do
|
210
|
+
stub_request(:get, uri).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'}).
|
211
|
+
to_return(**node_response)
|
212
|
+
|
213
|
+
Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'
|
214
|
+
Puppet[:profile] = true
|
215
|
+
|
216
|
+
subject.get_node(certname, environment: 'production')
|
217
|
+
end
|
218
|
+
|
207
219
|
it 'includes environment' do
|
208
220
|
stub_request(:get, uri)
|
209
221
|
.with(query: hash_including("environment" => "outerspace"))
|
@@ -271,9 +283,71 @@ describe Puppet::HTTP::Service::Compiler do
|
|
271
283
|
end
|
272
284
|
end
|
273
285
|
|
286
|
+
context 'when getting facts' do
|
287
|
+
let(:uri) { %r{/puppet/v3/facts/ziggy} }
|
288
|
+
let(:facts_response) { { body: formatter.render(facts), headers: {'Content-Type' => formatter.mime } } }
|
289
|
+
|
290
|
+
it 'includes environment' do
|
291
|
+
stub_request(:get, uri)
|
292
|
+
.with(query: hash_including("environment" => "outerspace"))
|
293
|
+
.to_return(**facts_response)
|
294
|
+
|
295
|
+
subject.get_facts(certname, environment: 'outerspace')
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'returns a deserialized facts object' do
|
299
|
+
stub_request(:get, uri)
|
300
|
+
.to_return(**facts_response)
|
301
|
+
|
302
|
+
n = subject.get_facts(certname, environment: 'production')
|
303
|
+
expect(n).to be_a(Puppet::Node::Facts)
|
304
|
+
expect(n.name).to eq(certname)
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'raises a response error if unsuccessful' do
|
308
|
+
stub_request(:get, uri)
|
309
|
+
.to_return(status: [500, "Server Error"])
|
310
|
+
|
311
|
+
expect {
|
312
|
+
subject.get_facts(certname, environment: 'production')
|
313
|
+
}.to raise_error do |err|
|
314
|
+
expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)
|
315
|
+
expect(err.message).to eq('Server Error')
|
316
|
+
expect(err.response.code).to eq(500)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
it 'raises a protocol error if the content-type header is missing' do
|
321
|
+
stub_request(:get, uri)
|
322
|
+
.to_return(body: "content-type is missing")
|
323
|
+
|
324
|
+
expect {
|
325
|
+
subject.get_facts(certname, environment: 'production')
|
326
|
+
}.to raise_error(Puppet::HTTP::ProtocolError, /No content type in http response; cannot parse/)
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'raises a serialization error if the content is invalid' do
|
330
|
+
stub_request(:get, uri)
|
331
|
+
.to_return(body: "this isn't valid JSON", headers: {'Content-Type' => 'application/json'})
|
332
|
+
|
333
|
+
expect {
|
334
|
+
subject.get_facts(certname, environment: 'production')
|
335
|
+
}.to raise_error(Puppet::HTTP::SerializationError, /Failed to deserialize Puppet::Node::Facts from json/)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
274
339
|
context 'when putting facts' do
|
275
340
|
let(:uri) { %r{/puppet/v3/facts/ziggy} }
|
276
341
|
|
342
|
+
it 'includes custom headers set the :http_extra_headers and :profile settings' do
|
343
|
+
stub_request(:put, uri).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'})
|
344
|
+
|
345
|
+
Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'
|
346
|
+
Puppet[:profile] = true
|
347
|
+
|
348
|
+
subject.put_facts(certname, environment: environment, facts: facts)
|
349
|
+
end
|
350
|
+
|
277
351
|
it 'serializes facts in the body' do
|
278
352
|
facts = Puppet::Node::Facts.new(certname, { 'domain' => 'zork'})
|
279
353
|
Puppet::Node::Facts.indirection.save(facts)
|
@@ -319,4 +393,57 @@ describe Puppet::HTTP::Service::Compiler do
|
|
319
393
|
}.to raise_error(Puppet::HTTP::SerializationError, /Failed to serialize Puppet::Node::Facts to json: "\\xE2" from ASCII-8BIT to UTF-8/)
|
320
394
|
end
|
321
395
|
end
|
396
|
+
|
397
|
+
context 'when getting status' do
|
398
|
+
let(:uri) { %r{/puppet/v3/status/ziggy} }
|
399
|
+
let(:status_response) { { body: formatter.render(status), headers: {'Content-Type' => formatter.mime } } }
|
400
|
+
|
401
|
+
it 'always sends production' do
|
402
|
+
stub_request(:get, uri)
|
403
|
+
.with(query: hash_including("environment" => "production"))
|
404
|
+
.to_return(**status_response)
|
405
|
+
|
406
|
+
subject.get_status(certname)
|
407
|
+
end
|
408
|
+
|
409
|
+
it 'returns a deserialized status' do
|
410
|
+
stub_request(:get, uri)
|
411
|
+
.to_return(**status_response)
|
412
|
+
|
413
|
+
s = subject.get_status(certname)
|
414
|
+
expect(s).to be_a(Puppet::Status)
|
415
|
+
expect(s.status).to eq("is_alive" => true)
|
416
|
+
end
|
417
|
+
|
418
|
+
it 'raises a response error if unsuccessful' do
|
419
|
+
stub_request(:get, uri)
|
420
|
+
.to_return(status: [500, "Server Error"])
|
421
|
+
|
422
|
+
expect {
|
423
|
+
subject.get_status(certname)
|
424
|
+
}.to raise_error do |err|
|
425
|
+
expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)
|
426
|
+
expect(err.message).to eq('Server Error')
|
427
|
+
expect(err.response.code).to eq(500)
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
it 'raises a protocol error if the content-type header is missing' do
|
432
|
+
stub_request(:get, uri)
|
433
|
+
.to_return(body: "content-type is missing")
|
434
|
+
|
435
|
+
expect {
|
436
|
+
subject.get_status(certname)
|
437
|
+
}.to raise_error(Puppet::HTTP::ProtocolError, /No content type in http response; cannot parse/)
|
438
|
+
end
|
439
|
+
|
440
|
+
it 'raises a serialization error if the content is invalid' do
|
441
|
+
stub_request(:get, uri)
|
442
|
+
.to_return(body: "this isn't valid JSON", headers: {'Content-Type' => 'application/json'})
|
443
|
+
|
444
|
+
expect {
|
445
|
+
subject.get_status(certname)
|
446
|
+
}.to raise_error(Puppet::HTTP::SerializationError, /Failed to deserialize Puppet::Status from json/)
|
447
|
+
end
|
448
|
+
end
|
322
449
|
end
|