puppet 6.10.1 → 6.11.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/Gemfile +4 -4
- data/Gemfile.lock +20 -12
- data/ext/project_data.yaml +3 -2
- data/ext/regexp_nodes/regexp_nodes.rb +4 -4
- data/ext/windows/service/daemon.rb +33 -8
- data/install.rb +6 -6
- data/lib/puppet.rb +8 -0
- data/lib/puppet/application.rb +1 -1
- data/lib/puppet/application/agent.rb +3 -0
- data/lib/puppet/application/apply.rb +2 -2
- data/lib/puppet/application/describe.rb +3 -9
- data/lib/puppet/application/device.rb +3 -0
- data/lib/puppet/application/doc.rb +1 -1
- data/lib/puppet/application/lookup.rb +1 -1
- data/lib/puppet/application/script.rb +2 -2
- data/lib/puppet/application/ssl.rb +25 -21
- data/lib/puppet/configurer.rb +42 -0
- data/lib/puppet/configurer/downloader.rb +2 -6
- data/lib/puppet/context/trusted_information.rb +42 -4
- data/lib/puppet/defaults.rb +19 -4
- data/lib/puppet/face/module/list.rb +5 -5
- data/lib/puppet/face/module/search.rb +1 -1
- data/lib/puppet/face/module/uninstall.rb +1 -1
- data/lib/puppet/face/module/upgrade.rb +1 -1
- data/lib/puppet/file_serving/http_metadata.rb +1 -1
- data/lib/puppet/file_system.rb +0 -8
- data/lib/puppet/file_system/memory_file.rb +1 -1
- data/lib/puppet/file_system/posix.rb +3 -2
- data/lib/puppet/forge.rb +3 -3
- data/lib/puppet/functions.rb +1 -2
- data/lib/puppet/gettext/module_translations.rb +1 -1
- data/lib/puppet/graph/rb_tree_map.rb +2 -2
- data/lib/puppet/graph/simple_graph.rb +4 -3
- data/lib/puppet/http.rb +29 -0
- data/lib/puppet/http/client.rb +156 -0
- data/lib/puppet/http/errors.rb +30 -0
- data/lib/puppet/http/redirector.rb +48 -0
- data/lib/puppet/http/resolver.rb +5 -0
- data/lib/puppet/http/resolver/settings.rb +5 -0
- data/lib/puppet/http/resolver/srv.rb +13 -0
- data/lib/puppet/http/response.rb +34 -0
- data/lib/puppet/http/retry_after_handler.rb +47 -0
- data/lib/puppet/http/service.rb +18 -0
- data/lib/puppet/http/service/ca.rb +49 -0
- data/lib/puppet/http/session.rb +55 -0
- data/lib/puppet/indirector/file_bucket_file/file.rb +1 -1
- data/lib/puppet/indirector/hiera.rb +2 -0
- data/lib/puppet/indirector/request.rb +1 -1
- data/lib/puppet/indirector/resource/ral.rb +1 -3
- data/lib/puppet/indirector/resource/validator.rb +1 -1
- data/lib/puppet/interface.rb +2 -1
- data/lib/puppet/interface/documentation.rb +1 -1
- data/lib/puppet/loaders.rb +0 -1
- data/lib/puppet/metatype/manager.rb +1 -1
- data/lib/puppet/module.rb +1 -1
- data/lib/puppet/module/task.rb +20 -4
- data/lib/puppet/module_tool/applications/installer.rb +1 -1
- data/lib/puppet/module_tool/applications/uninstaller.rb +3 -3
- data/lib/puppet/module_tool/metadata.rb +1 -1
- data/lib/puppet/module_tool/shared_behaviors.rb +4 -4
- data/lib/puppet/module_tool/tar/mini.rb +1 -1
- data/lib/puppet/network/http.rb +2 -6
- data/lib/puppet/network/http/api/indirected_routes.rb +12 -11
- data/lib/puppet/network/http/connection.rb +10 -12
- data/lib/puppet/network/http/pool.rb +2 -0
- data/lib/puppet/network/http/site.rb +5 -1
- data/lib/puppet/network/resolver.rb +4 -4
- data/lib/puppet/node/environment.rb +4 -2
- data/lib/puppet/pal/pal_impl.rb +2 -2
- data/lib/puppet/parser/ast.rb +1 -1
- data/lib/puppet/parser/ast/resourceparam.rb +1 -1
- data/lib/puppet/parser/functions.rb +1 -1
- data/lib/puppet/parser/scope.rb +8 -7
- data/lib/puppet/pops/evaluator/collectors/catalog_collector.rb +1 -1
- data/lib/puppet/pops/evaluator/collectors/exported_collector.rb +1 -1
- data/lib/puppet/pops/evaluator/external_syntax_support.rb +3 -2
- data/lib/puppet/pops/evaluator/runtime3_support.rb +4 -7
- data/lib/puppet/pops/loader/module_loaders.rb +1 -1
- data/lib/puppet/pops/loader/task_instantiator.rb +4 -0
- data/lib/puppet/pops/loaders.rb +1 -1
- data/lib/puppet/pops/lookup/hiera_config.rb +1 -0
- data/lib/puppet/pops/lookup/sub_lookup.rb +1 -1
- data/lib/puppet/pops/merge_strategy.rb +22 -18
- data/lib/puppet/pops/parser/heredoc_support.rb +1 -1
- data/lib/puppet/pops/parser/interpolation_support.rb +4 -4
- data/lib/puppet/pops/parser/locator.rb +1 -1
- data/lib/puppet/pops/parser/pn_parser.rb +17 -16
- data/lib/puppet/pops/puppet_stack.rb +52 -48
- data/lib/puppet/pops/types/p_sensitive_type.rb +1 -1
- data/lib/puppet/pops/types/p_uri_type.rb +1 -1
- data/lib/puppet/pops/types/string_converter.rb +10 -10
- data/lib/puppet/pops/types/types.rb +3 -3
- data/lib/puppet/property.rb +1 -1
- data/lib/puppet/property/ensure.rb +1 -1
- data/lib/puppet/provider/exec.rb +6 -2
- data/lib/puppet/provider/nameservice/directoryservice.rb +1 -1
- data/lib/puppet/provider/nameservice/pw.rb +2 -2
- data/lib/puppet/provider/package/apt.rb +5 -1
- data/lib/puppet/provider/package/dnfmodule.rb +87 -0
- data/lib/puppet/provider/package/dpkg.rb +31 -17
- data/lib/puppet/provider/package/openbsd.rb +1 -1
- data/lib/puppet/provider/package/pip.rb +34 -9
- data/lib/puppet/provider/package/portage.rb +1 -1
- data/lib/puppet/provider/package/rpm.rb +5 -5
- data/lib/puppet/provider/package/windows/package.rb +1 -1
- data/lib/puppet/provider/package/yum.rb +1 -1
- data/lib/puppet/provider/parsedfile.rb +1 -1
- data/lib/puppet/provider/service/daemontools.rb +9 -9
- data/lib/puppet/provider/service/openbsd.rb +1 -1
- data/lib/puppet/provider/service/rcng.rb +2 -2
- data/lib/puppet/provider/service/runit.rb +2 -8
- data/lib/puppet/provider/service/systemd.rb +10 -10
- data/lib/puppet/provider/user/directoryservice.rb +1 -1
- data/lib/puppet/provider/user/user_role_add.rb +1 -1
- data/lib/puppet/provider/user/useradd.rb +22 -13
- data/lib/puppet/provider/user/windows_adsi.rb +4 -5
- data/lib/puppet/reference/indirection.rb +2 -2
- data/lib/puppet/reference/metaparameter.rb +1 -3
- data/lib/puppet/reference/providers.rb +1 -1
- data/lib/puppet/reference/type.rb +3 -9
- data/lib/puppet/reports.rb +1 -1
- data/lib/puppet/resource.rb +1 -1
- data/lib/puppet/resource/catalog.rb +1 -1
- data/lib/puppet/rest/errors.rb +1 -0
- data/lib/puppet/rest/response.rb +1 -0
- data/lib/puppet/rest/route.rb +1 -0
- data/lib/puppet/rest/routes.rb +3 -0
- data/lib/puppet/runtime.rb +25 -0
- data/lib/puppet/settings.rb +3 -3
- data/lib/puppet/settings/environment_conf.rb +1 -0
- data/lib/puppet/ssl/host.rb +1 -1
- data/lib/puppet/ssl/oids.rb +1 -1
- data/lib/puppet/ssl/state_machine.rb +23 -15
- data/lib/puppet/test/test_helper.rb +1 -1
- data/lib/puppet/transaction/report.rb +1 -1
- data/lib/puppet/trusted_external.rb +13 -0
- data/lib/puppet/type.rb +1 -3
- data/lib/puppet/type/exec.rb +7 -3
- data/lib/puppet/type/file.rb +1 -2
- data/lib/puppet/type/file/source.rb +2 -2
- data/lib/puppet/type/package.rb +10 -3
- data/lib/puppet/type/schedule.rb +1 -1
- data/lib/puppet/type/service.rb +1 -1
- data/lib/puppet/util.rb +2 -2
- data/lib/puppet/util/command_line/trollop.rb +1 -1
- data/lib/puppet/util/http_proxy.rb +2 -10
- data/lib/puppet/util/log.rb +2 -2
- data/lib/puppet/util/log/destinations.rb +2 -2
- data/lib/puppet/util/logging.rb +2 -2
- data/lib/puppet/util/metric.rb +2 -2
- data/lib/puppet/util/platform.rb +15 -4
- data/lib/puppet/util/provider_features.rb +2 -4
- data/lib/puppet/util/rdoc.rb +1 -1
- data/lib/puppet/util/reference.rb +1 -1
- data/lib/puppet/util/resource_template.rb +1 -1
- data/lib/puppet/util/selinux.rb +3 -1
- data/lib/puppet/util/windows/registry.rb +7 -5
- data/lib/puppet/vendor.rb +1 -1
- data/lib/puppet/vendor/require_vendored.rb +0 -1
- data/lib/puppet/version.rb +1 -1
- data/lib/puppet/x509/cert_provider.rb +4 -1
- data/locales/puppet.pot +279 -203
- data/man/man5/puppet.conf.5 +30 -8
- data/man/man8/puppet-agent.8 +4 -1
- 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-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 +1 -1
- data/man/man8/puppet-status.8 +1 -1
- data/man/man8/puppet.8 +2 -2
- data/spec/fixtures/unit/provider/package/dnfmodule/dnf-module-list-installed.txt +11 -0
- data/spec/integration/configurer_spec.rb +52 -0
- data/spec/lib/puppet/certificate_factory.rb +2 -2
- data/spec/spec_helper.rb +24 -0
- data/spec/unit/application/device_spec.rb +6 -0
- data/spec/unit/application/ssl_spec.rb +4 -7
- data/spec/unit/configurer_spec.rb +1 -0
- data/spec/unit/context/trusted_information_spec.rb +41 -2
- data/spec/unit/http/client_spec.rb +440 -0
- data/spec/unit/http/resolver_spec.rb +45 -0
- data/spec/unit/http/service/ca_spec.rb +106 -0
- data/spec/unit/http/service_spec.rb +32 -0
- data/spec/unit/http/session_spec.rb +102 -0
- data/spec/unit/indirector/resource/ral_spec.rb +4 -4
- data/spec/unit/network/http/connection_spec.rb +119 -145
- data/spec/unit/network/http/site_spec.rb +7 -0
- data/spec/unit/parser/scope_spec.rb +10 -0
- data/spec/unit/pops/loaders/loaders_spec.rb +13 -2
- data/spec/unit/pops/loaders/module_loaders_spec.rb +37 -0
- data/spec/unit/provider/exec_spec.rb +209 -0
- data/spec/unit/provider/package/dnfmodule_spec.rb +186 -0
- data/spec/unit/provider/package/dpkg_spec.rb +238 -78
- data/spec/unit/provider/package/pip_spec.rb +51 -6
- data/spec/unit/provider/service/daemontools_spec.rb +24 -0
- data/spec/unit/provider/service/runit_spec.rb +24 -0
- data/spec/unit/provider/service/systemd_spec.rb +25 -25
- data/spec/unit/provider/user/useradd_spec.rb +46 -0
- data/spec/unit/ssl/host_spec.rb +0 -5
- data/spec/unit/ssl/state_machine_spec.rb +16 -10
- data/spec/unit/type/exec_spec.rb +6 -12
- data/spec/unit/type/file_spec.rb +9 -4
- data/spec/unit/type/package_spec.rb +5 -0
- data/spec/unit/util/execution_spec.rb +16 -0
- data/spec/unit/util/http_proxy_spec.rb +79 -27
- data/spec/unit/util/log/destinations_spec.rb +7 -3
- metadata +45 -22
- data/lib/puppet/pops/loader/null_loader.rb +0 -60
- data/lib/puppet/vendor/deep_merge/CHANGELOG +0 -45
- data/lib/puppet/vendor/deep_merge/Gemfile +0 -3
- data/lib/puppet/vendor/deep_merge/LICENSE +0 -21
- data/lib/puppet/vendor/deep_merge/PUPPET_README.md +0 -6
- data/lib/puppet/vendor/deep_merge/README.md +0 -113
- data/lib/puppet/vendor/deep_merge/Rakefile +0 -19
- data/lib/puppet/vendor/deep_merge/deep_merge.gemspec +0 -35
- data/lib/puppet/vendor/deep_merge/lib/deep_merge.rb +0 -2
- data/lib/puppet/vendor/deep_merge/lib/deep_merge/core.rb +0 -210
- data/lib/puppet/vendor/deep_merge/lib/deep_merge/deep_merge_hash.rb +0 -28
- data/lib/puppet/vendor/deep_merge/lib/deep_merge/rails_compat.rb +0 -27
- data/lib/puppet/vendor/deep_merge/test/test_deep_merge.rb +0 -608
- data/lib/puppet/vendor/load_deep_merge.rb +0 -1
- data/spec/fixtures/vcr/cassettes/Puppet_Network_HTTP_Connection/when_handling_requests/_request_get/should_yield_to_the_block.yml +0 -24
- data/spec/fixtures/vcr/cassettes/Puppet_Network_HTTP_Connection/when_handling_requests/_request_head/should_yield_to_the_block.yml +0 -24
- data/spec/fixtures/vcr/cassettes/Puppet_Network_HTTP_Connection/when_handling_requests/_request_post/should_yield_to_the_block.yml +0 -24
@@ -170,6 +170,12 @@ describe Puppet::Application::Device do
|
|
170
170
|
|
171
171
|
device.handle_facts(true)
|
172
172
|
end
|
173
|
+
|
174
|
+
it "should register ssl OIDs" do
|
175
|
+
expect(Puppet::SSL::Oids).to receive(:register_puppet_oids)
|
176
|
+
|
177
|
+
device.setup
|
178
|
+
end
|
173
179
|
end
|
174
180
|
|
175
181
|
describe "during setup" do
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'puppet/application/ssl'
|
3
|
-
require 'webmock/rspec'
|
4
3
|
require 'openssl'
|
5
4
|
require 'puppet/test_ca'
|
6
5
|
|
@@ -23,11 +22,6 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
|
|
23
22
|
end
|
24
23
|
|
25
24
|
before do
|
26
|
-
WebMock.disable_net_connect!
|
27
|
-
|
28
|
-
allow_any_instance_of(Net::HTTP).to receive(:start)
|
29
|
-
allow_any_instance_of(Net::HTTP).to receive(:finish)
|
30
|
-
|
31
25
|
Puppet.settings.use(:main)
|
32
26
|
Puppet[:certname] = name
|
33
27
|
Puppet[:vardir] = tmpdir("ssl_testing")
|
@@ -126,8 +120,11 @@ describe Puppet::Application::Ssl, unless: Puppet::Util::Platform.jruby? do
|
|
126
120
|
end
|
127
121
|
|
128
122
|
it 'registers OIDs' do
|
123
|
+
stub_request(:put, %r{puppet-ca/v1/certificate_request/#{name}}).to_return(status: 200)
|
124
|
+
stub_request(:get, %r{puppet-ca/v1/certificate/#{name}}).to_return(status: 404)
|
125
|
+
|
129
126
|
expect(Puppet::SSL::Oids).to receive(:register_puppet_oids)
|
130
|
-
|
127
|
+
expects_command_to_pass(%r{Submitted certificate request for '#{name}' to https://.*})
|
131
128
|
end
|
132
129
|
|
133
130
|
it 'submits the CSR and saves it locally' do
|
@@ -30,6 +30,27 @@ describe Puppet::Context::TrustedInformation, :unless => RUBY_PLATFORM == 'java'
|
|
30
30
|
cert
|
31
31
|
end
|
32
32
|
|
33
|
+
let(:external_data) {
|
34
|
+
{
|
35
|
+
'string' => 'a',
|
36
|
+
'integer' => 1,
|
37
|
+
'boolean' => true,
|
38
|
+
'hash' => { 'one' => 'two' },
|
39
|
+
'array' => ['b', 2, {}]
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
def allow_external_trusted_data(certname, data)
|
44
|
+
Puppet[:trusted_external_command] = '/usr/bin/generate_data.sh'
|
45
|
+
allow(Puppet::Util::Execution).to receive(:execute).with(['/usr/bin/generate_data.sh', certname], anything).and_return(JSON.dump(data))
|
46
|
+
end
|
47
|
+
|
48
|
+
it "defaults external to an empty hash" do
|
49
|
+
trusted = Puppet::Context::TrustedInformation.new(false, 'ignored', nil)
|
50
|
+
|
51
|
+
expect(trusted.external).to eq({})
|
52
|
+
end
|
53
|
+
|
33
54
|
context "when remote" do
|
34
55
|
it "has no cert information when it isn't authenticated" do
|
35
56
|
trusted = Puppet::Context::TrustedInformation.remote(false, 'ignored', nil)
|
@@ -61,6 +82,14 @@ describe Puppet::Context::TrustedInformation, :unless => RUBY_PLATFORM == 'java'
|
|
61
82
|
expect(trusted.certname).to eq('cert name')
|
62
83
|
expect(trusted.extensions).to eq({})
|
63
84
|
end
|
85
|
+
|
86
|
+
it 'contains external trusted data' do
|
87
|
+
allow_external_trusted_data('cert name', external_data)
|
88
|
+
|
89
|
+
trusted = Puppet::Context::TrustedInformation.remote(true, 'cert name', nil)
|
90
|
+
|
91
|
+
expect(trusted.external).to eq(external_data)
|
92
|
+
end
|
64
93
|
end
|
65
94
|
|
66
95
|
context "when local" do
|
@@ -85,6 +114,14 @@ describe Puppet::Context::TrustedInformation, :unless => RUBY_PLATFORM == 'java'
|
|
85
114
|
expect(trusted.hostname).to be_nil
|
86
115
|
expect(trusted.domain).to be_nil
|
87
116
|
end
|
117
|
+
|
118
|
+
it 'contains external trusted data' do
|
119
|
+
allow_external_trusted_data('cert name', external_data)
|
120
|
+
|
121
|
+
trusted = Puppet::Context::TrustedInformation.remote(true, 'cert name', nil)
|
122
|
+
|
123
|
+
expect(trusted.external).to eq(external_data)
|
124
|
+
end
|
88
125
|
end
|
89
126
|
|
90
127
|
it "converts itself to a hash" do
|
@@ -98,7 +135,8 @@ describe Puppet::Context::TrustedInformation, :unless => RUBY_PLATFORM == 'java'
|
|
98
135
|
'1.3.6.1.4.1.34380.1.2.2' => 'more CSR specific info',
|
99
136
|
},
|
100
137
|
'hostname' => 'cert name',
|
101
|
-
'domain' => nil
|
138
|
+
'domain' => nil,
|
139
|
+
'external' => {},
|
102
140
|
})
|
103
141
|
end
|
104
142
|
|
@@ -113,7 +151,8 @@ describe Puppet::Context::TrustedInformation, :unless => RUBY_PLATFORM == 'java'
|
|
113
151
|
'1.3.6.1.4.1.34380.1.2.2' => 'more CSR specific info',
|
114
152
|
},
|
115
153
|
'hostname' => 'hostname',
|
116
|
-
'domain' => 'domain.long'
|
154
|
+
'domain' => 'domain.long',
|
155
|
+
'external' => {},
|
117
156
|
})
|
118
157
|
end
|
119
158
|
|
@@ -0,0 +1,440 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'webmock/rspec'
|
3
|
+
require 'puppet/http'
|
4
|
+
|
5
|
+
describe Puppet::HTTP::Client do
|
6
|
+
let(:uri) { URI.parse('https://www.example.com') }
|
7
|
+
let(:client) { described_class.new }
|
8
|
+
let(:credentials) { ['user', 'pass'] }
|
9
|
+
|
10
|
+
it 'creates unique sessions' do
|
11
|
+
expect(client.create_session).to_not eq(client.create_session)
|
12
|
+
end
|
13
|
+
|
14
|
+
context "when connecting" do
|
15
|
+
it 'connects to HTTP URLs' do
|
16
|
+
uri = URI.parse('http://www.example.com')
|
17
|
+
|
18
|
+
client.connect(uri) do |http|
|
19
|
+
expect(http.address).to eq('www.example.com')
|
20
|
+
expect(http.port).to eq(80)
|
21
|
+
expect(http).to_not be_use_ssl
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'connects to HTTPS URLs' do
|
26
|
+
client.connect(uri) do |http|
|
27
|
+
expect(http.address).to eq('www.example.com')
|
28
|
+
expect(http.port).to eq(443)
|
29
|
+
expect(http).to be_use_ssl
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'raises ConnectionError if the connection is refused' do
|
34
|
+
allow_any_instance_of(Net::HTTP).to receive(:start).and_raise(Errno::ECONNREFUSED)
|
35
|
+
|
36
|
+
expect {
|
37
|
+
client.connect(uri)
|
38
|
+
}.to raise_error(Puppet::HTTP::ConnectionError, %r{Failed to connect to https://www.example.com:})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'after connecting' do
|
43
|
+
def expect_http_error(cause, expected_message)
|
44
|
+
expect {
|
45
|
+
client.connect(uri) do |_|
|
46
|
+
raise cause, 'whoops'
|
47
|
+
end
|
48
|
+
}.to raise_error(Puppet::HTTP::HTTPError, expected_message)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 're-raises HTTPError' do
|
52
|
+
expect_http_error(Puppet::HTTP::HTTPError, 'whoops')
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'raises HTTPError if connection is interrupted while reading' do
|
56
|
+
expect_http_error(EOFError, %r{Request to https://www.example.com interrupted after .* seconds})
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'raises HTTPError if connection times out' do
|
60
|
+
expect_http_error(Net::ReadTimeout, %r{Request to https://www.example.com timed out after .* seconds})
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'raises HTTPError if connection fails' do
|
64
|
+
expect_http_error(ArgumentError, %r{Request to https://www.example.com failed after .* seconds})
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "when closing" do
|
69
|
+
it "closes all connections in the pool" do
|
70
|
+
pool = double('pool')
|
71
|
+
expect(pool).to receive(:close)
|
72
|
+
|
73
|
+
client = described_class.new(pool: pool)
|
74
|
+
client.close
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "for GET requests" do
|
79
|
+
it "includes default HTTP headers" do
|
80
|
+
stub_request(:get, uri).with(headers: {'X-Puppet-Version' => /./, 'User-Agent' => /./})
|
81
|
+
|
82
|
+
client.get(uri)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "stringifies keys and encodes values in the query" do
|
86
|
+
stub_request(:get, uri).with(query: "foo=bar%3Dbaz")
|
87
|
+
|
88
|
+
client.get(uri, params: {:foo => "bar=baz"})
|
89
|
+
end
|
90
|
+
|
91
|
+
it "merges custom headers with default ones" do
|
92
|
+
stub_request(:get, uri).with(headers: { 'X-Foo' => 'Bar', 'X-Puppet-Version' => /./, 'User-Agent' => /./ })
|
93
|
+
|
94
|
+
client.get(uri, headers: {'X-Foo' => 'Bar'})
|
95
|
+
end
|
96
|
+
|
97
|
+
it "returns the response" do
|
98
|
+
stub_request(:get, uri)
|
99
|
+
|
100
|
+
response = client.get(uri)
|
101
|
+
expect(response).to be_an_instance_of(Puppet::HTTP::Response)
|
102
|
+
expect(response).to be_success
|
103
|
+
expect(response.code).to eq(200)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "returns the entire response body" do
|
107
|
+
stub_request(:get, uri).to_return(body: "abc")
|
108
|
+
|
109
|
+
expect(client.get(uri).body).to eq("abc")
|
110
|
+
end
|
111
|
+
|
112
|
+
it "streams the response body when a block is given" do
|
113
|
+
stub_request(:get, uri).to_return(body: "abc")
|
114
|
+
|
115
|
+
io = StringIO.new
|
116
|
+
client.get(uri) do |response|
|
117
|
+
response.read_body do |data|
|
118
|
+
io.write(data)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
expect(io.string).to eq("abc")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "for PUT requests" do
|
127
|
+
it "includes default HTTP headers" do
|
128
|
+
stub_request(:put, uri).with(headers: {'X-Puppet-Version' => /./, 'User-Agent' => /./})
|
129
|
+
|
130
|
+
client.put(uri, content_type: 'text/plain', body: "")
|
131
|
+
end
|
132
|
+
|
133
|
+
it "stringifies keys and encodes values in the query" do
|
134
|
+
stub_request(:put, "https://www.example.com").with(query: "foo=bar%3Dbaz")
|
135
|
+
|
136
|
+
client.put(uri, params: {:foo => "bar=baz"}, content_type: 'text/plain', body: "")
|
137
|
+
end
|
138
|
+
|
139
|
+
it "includes custom headers" do
|
140
|
+
stub_request(:put, "https://www.example.com").with(headers: { 'X-Foo' => 'Bar' })
|
141
|
+
|
142
|
+
client.put(uri, headers: {'X-Foo' => 'Bar'}, content_type: 'text/plain', body: "")
|
143
|
+
end
|
144
|
+
|
145
|
+
it "returns the response" do
|
146
|
+
stub_request(:put, uri)
|
147
|
+
|
148
|
+
response = client.put(uri, content_type: 'text/plain', body: "")
|
149
|
+
expect(response).to be_an_instance_of(Puppet::HTTP::Response)
|
150
|
+
expect(response).to be_success
|
151
|
+
expect(response.code).to eq(200)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "sets content-length and content-type for the body" do
|
155
|
+
stub_request(:put, uri).with(headers: {"Content-Length" => "5", "Content-Type" => "text/plain"})
|
156
|
+
|
157
|
+
client.put(uri, content_type: 'text/plain', body: "hello")
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context "Basic Auth" do
|
162
|
+
it "submits credentials for GET requests" do
|
163
|
+
stub_request(:get, uri).with(basic_auth: credentials)
|
164
|
+
|
165
|
+
client.get(uri, user: 'user', password: 'pass')
|
166
|
+
end
|
167
|
+
|
168
|
+
it "submits credentials for PUT requests" do
|
169
|
+
stub_request(:put, uri).with(basic_auth: credentials)
|
170
|
+
|
171
|
+
client.put(uri, content_type: 'text/plain', body: "hello", user: 'user', password: 'pass')
|
172
|
+
end
|
173
|
+
|
174
|
+
it "returns response containing access denied" do
|
175
|
+
stub_request(:get, uri).with(basic_auth: credentials).to_return(status: [403, "Ye Shall Not Pass"])
|
176
|
+
|
177
|
+
response = client.get(uri, user: 'user', password: 'pass')
|
178
|
+
expect(response.code).to eq(403)
|
179
|
+
expect(response.reason).to eq("Ye Shall Not Pass")
|
180
|
+
expect(response).to_not be_success
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'omits basic auth if user is nil' do
|
184
|
+
stub_request(:get, uri).with do |req|
|
185
|
+
expect(req.headers).to_not include('Authorization')
|
186
|
+
end
|
187
|
+
|
188
|
+
client.get(uri, user: nil, password: 'pass')
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'omits basic auth if password is nil' do
|
192
|
+
stub_request(:get, uri).with do |req|
|
193
|
+
expect(req.headers).to_not include('Authorization')
|
194
|
+
end
|
195
|
+
|
196
|
+
client.get(uri, user: 'user', password: nil)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context "when redirecting" do
|
201
|
+
let(:start_url) { URI("https://www.example.com:8140/foo") }
|
202
|
+
let(:bar_url) { "https://www.example.com:8140/bar" }
|
203
|
+
let(:baz_url) { "https://www.example.com:8140/baz" }
|
204
|
+
let(:other_host) { "https://other.example.com:8140/qux" }
|
205
|
+
|
206
|
+
def redirect_to(status: 302, url:)
|
207
|
+
{ status: status, headers: { 'Location' => url }, body: "Redirected to #{url}" }
|
208
|
+
end
|
209
|
+
|
210
|
+
it "preserves GET method" do
|
211
|
+
stub_request(:get, start_url).to_return(redirect_to(url: bar_url))
|
212
|
+
stub_request(:get, bar_url).to_return(status: 200)
|
213
|
+
|
214
|
+
response = client.get(start_url)
|
215
|
+
expect(response).to be_success
|
216
|
+
end
|
217
|
+
|
218
|
+
it "preserves PUT method" do
|
219
|
+
stub_request(:put, start_url).to_return(redirect_to(url: bar_url))
|
220
|
+
stub_request(:put, bar_url).to_return(status: 200)
|
221
|
+
|
222
|
+
response = client.put(start_url, body: "", content_type: 'text/plain')
|
223
|
+
expect(response).to be_success
|
224
|
+
end
|
225
|
+
|
226
|
+
it "preserves query parameters" do
|
227
|
+
query = { 'debug' => true }
|
228
|
+
stub_request(:get, start_url).with(query: query).to_return(redirect_to(url: bar_url))
|
229
|
+
stub_request(:get, bar_url).with(query: query).to_return(status: 200)
|
230
|
+
|
231
|
+
response = client.get(start_url, params: query)
|
232
|
+
expect(response).to be_success
|
233
|
+
end
|
234
|
+
|
235
|
+
it "preserves custom and default headers when redirecting" do
|
236
|
+
headers = { 'X-Foo' => 'Bar', 'X-Puppet-Version' => Puppet.version }
|
237
|
+
stub_request(:get, start_url).with(headers: headers).to_return(redirect_to(url: bar_url))
|
238
|
+
stub_request(:get, bar_url).with(headers: headers).to_return(status: 200)
|
239
|
+
|
240
|
+
response = client.get(start_url, headers: headers)
|
241
|
+
expect(response).to be_success
|
242
|
+
end
|
243
|
+
|
244
|
+
it "preserves basic authorization" do
|
245
|
+
stub_request(:get, start_url).with(basic_auth: credentials).to_return(redirect_to(url: bar_url))
|
246
|
+
stub_request(:get, bar_url).with(basic_auth: credentials).to_return(status: 200)
|
247
|
+
|
248
|
+
client.get(start_url, user: 'user', password: 'pass')
|
249
|
+
end
|
250
|
+
|
251
|
+
it "redirects given a relative location" do
|
252
|
+
relative_url = "/people.html"
|
253
|
+
stub_request(:get, start_url).to_return(redirect_to(url: relative_url))
|
254
|
+
stub_request(:get, "https://www.example.com:8140/people.html").to_return(status: 200)
|
255
|
+
|
256
|
+
response = client.get(start_url)
|
257
|
+
expect(response).to be_success
|
258
|
+
end
|
259
|
+
|
260
|
+
it "preserves query parameters given a relative location" do
|
261
|
+
relative_url = "/people.html"
|
262
|
+
query = { 'debug' => true }
|
263
|
+
stub_request(:get, start_url).with(query: query).to_return(redirect_to(url: relative_url))
|
264
|
+
stub_request(:get, "https://www.example.com:8140/people.html").with(query: query).to_return(status: 200)
|
265
|
+
|
266
|
+
response = client.get(start_url, params: query)
|
267
|
+
expect(response).to be_success
|
268
|
+
end
|
269
|
+
|
270
|
+
it "preserves request body for each request" do
|
271
|
+
data = 'some data'
|
272
|
+
stub_request(:put, start_url).with(body: data).to_return(redirect_to(url: bar_url))
|
273
|
+
stub_request(:put, bar_url).with(body: data).to_return(status: 200)
|
274
|
+
|
275
|
+
response = client.put(start_url, body: data, content_type: 'text/plain')
|
276
|
+
expect(response).to be_success
|
277
|
+
end
|
278
|
+
|
279
|
+
it "returns the body from the final response" do
|
280
|
+
stub_request(:get, start_url).to_return(redirect_to(url: bar_url))
|
281
|
+
stub_request(:get, bar_url).to_return(status: 200, body: 'followed')
|
282
|
+
|
283
|
+
response = client.get(start_url)
|
284
|
+
expect(response.body).to eq('followed')
|
285
|
+
end
|
286
|
+
|
287
|
+
[301, 307].each do |code|
|
288
|
+
it "also redirects on #{code}" do
|
289
|
+
stub_request(:get, start_url).to_return(redirect_to(status: code, url: bar_url))
|
290
|
+
stub_request(:get, bar_url).to_return(status: 200)
|
291
|
+
|
292
|
+
response = client.get(start_url)
|
293
|
+
expect(response).to be_success
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
[303, 308].each do |code|
|
298
|
+
it "returns an error on #{code}" do
|
299
|
+
stub_request(:get, start_url).to_return(redirect_to(status: code, url: bar_url))
|
300
|
+
|
301
|
+
response = client.get(start_url)
|
302
|
+
expect(response.code).to eq(code)
|
303
|
+
expect(response).to_not be_success
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
it "raises an error if the Location header is missing" do
|
308
|
+
stub_request(:get, start_url).to_return(status: 302)
|
309
|
+
|
310
|
+
expect {
|
311
|
+
client.get(start_url)
|
312
|
+
}.to raise_error(Puppet::HTTP::ProtocolError, "Location response header is missing")
|
313
|
+
end
|
314
|
+
|
315
|
+
it "raises an error if the Location header is invalid" do
|
316
|
+
stub_request(:get, start_url).to_return(redirect_to(status: 302, url: 'http://foo"bar'))
|
317
|
+
|
318
|
+
expect {
|
319
|
+
client.get(start_url)
|
320
|
+
}.to raise_error(Puppet::HTTP::ProtocolError, /Location URI is invalid/)
|
321
|
+
end
|
322
|
+
|
323
|
+
it "raises an error if limit is 0 and we're asked to follow" do
|
324
|
+
stub_request(:get, start_url).to_return(redirect_to(url: bar_url))
|
325
|
+
|
326
|
+
client = described_class.new(redirect_limit: 0)
|
327
|
+
expect {
|
328
|
+
client.get(start_url)
|
329
|
+
}.to raise_error(Puppet::HTTP::TooManyRedirects, %r{Too many HTTP redirections for https://www.example.com:8140})
|
330
|
+
end
|
331
|
+
|
332
|
+
it "raises an error if asked to follow redirects more times than the limit" do
|
333
|
+
stub_request(:get, start_url).to_return(redirect_to(url: bar_url))
|
334
|
+
stub_request(:get, bar_url).to_return(redirect_to(url: baz_url))
|
335
|
+
|
336
|
+
client = described_class.new(redirect_limit: 1)
|
337
|
+
expect {
|
338
|
+
client.get(start_url)
|
339
|
+
}.to raise_error(Puppet::HTTP::TooManyRedirects, %r{Too many HTTP redirections for https://www.example.com:8140})
|
340
|
+
end
|
341
|
+
|
342
|
+
it "follows multiple redirects if equal to or less than the redirect limit" do
|
343
|
+
stub_request(:get, start_url).to_return(redirect_to(url: bar_url))
|
344
|
+
stub_request(:get, bar_url).to_return(redirect_to(url: baz_url))
|
345
|
+
stub_request(:get, baz_url).to_return(status: 200)
|
346
|
+
|
347
|
+
client = described_class.new(redirect_limit: 2)
|
348
|
+
response = client.get(start_url)
|
349
|
+
expect(response).to be_success
|
350
|
+
end
|
351
|
+
|
352
|
+
it "redirects to a different host" do
|
353
|
+
stub_request(:get, start_url).to_return(redirect_to(url: other_host))
|
354
|
+
stub_request(:get, other_host).to_return(status: 200)
|
355
|
+
|
356
|
+
response = client.get(start_url)
|
357
|
+
expect(response).to be_success
|
358
|
+
end
|
359
|
+
|
360
|
+
it "redirects from http to https" do
|
361
|
+
http = URI("http://example.com/foo")
|
362
|
+
https = URI("https://example.com/bar")
|
363
|
+
|
364
|
+
stub_request(:get, http).to_return(redirect_to(url: https))
|
365
|
+
stub_request(:get, https).to_return(status: 200)
|
366
|
+
|
367
|
+
response = client.get(http)
|
368
|
+
expect(response).to be_success
|
369
|
+
end
|
370
|
+
|
371
|
+
it "redirects from https to http" do
|
372
|
+
http = URI("http://example.com/foo")
|
373
|
+
https = URI("https://example.com/bar")
|
374
|
+
|
375
|
+
stub_request(:get, https).to_return(redirect_to(url: http))
|
376
|
+
stub_request(:get, http).to_return(status: 200)
|
377
|
+
|
378
|
+
response = client.get(https)
|
379
|
+
expect(response).to be_success
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
context "when response indicates an overloaded server" do
|
384
|
+
def retry_after(datetime)
|
385
|
+
stub_request(:get, uri)
|
386
|
+
.to_return(status: [503, 'Service Unavailable'], headers: {'Retry-After' => datetime}).then
|
387
|
+
.to_return(status: 200)
|
388
|
+
end
|
389
|
+
|
390
|
+
it "returns a 503 response if Retry-After is not set" do
|
391
|
+
stub_request(:get, uri).to_return(status: [503, 'Service Unavailable'])
|
392
|
+
|
393
|
+
expect(client.get(uri).code).to eq(503)
|
394
|
+
end
|
395
|
+
|
396
|
+
it "raises if Retry-After is not convertible to an Integer or RFC 2822 Date" do
|
397
|
+
stub_request(:get, uri).to_return(status: [503, 'Service Unavailable'], headers: {'Retry-After' => 'foo'})
|
398
|
+
|
399
|
+
expect {
|
400
|
+
client.get(uri)
|
401
|
+
}.to raise_error(Puppet::HTTP::ProtocolError, /Failed to parse Retry-After header 'foo' as an integer or RFC 2822 date/)
|
402
|
+
end
|
403
|
+
|
404
|
+
it "should sleep and retry if Retry-After is an Integer" do
|
405
|
+
retry_after('42')
|
406
|
+
|
407
|
+
expect(::Kernel).to receive(:sleep).with(42)
|
408
|
+
|
409
|
+
client.get(uri)
|
410
|
+
end
|
411
|
+
|
412
|
+
it "should sleep and retry if Retry-After is an RFC 2822 Date" do
|
413
|
+
retry_after('Wed, 13 Apr 2005 15:18:05 GMT')
|
414
|
+
|
415
|
+
now = DateTime.new(2005, 4, 13, 8, 17, 5, '-07:00')
|
416
|
+
allow(DateTime).to receive(:now).and_return(now)
|
417
|
+
|
418
|
+
expect(::Kernel).to receive(:sleep).with(60)
|
419
|
+
|
420
|
+
client.get(uri)
|
421
|
+
end
|
422
|
+
|
423
|
+
it "should sleep for no more than the Puppet runinterval" do
|
424
|
+
retry_after('60')
|
425
|
+
Puppet[:runinterval] = 30
|
426
|
+
|
427
|
+
expect(::Kernel).to receive(:sleep).with(30)
|
428
|
+
|
429
|
+
client.get(uri)
|
430
|
+
end
|
431
|
+
|
432
|
+
it "should sleep for 0 seconds if the RFC 2822 date has past" do
|
433
|
+
retry_after('Wed, 13 Apr 2005 15:18:05 GMT')
|
434
|
+
|
435
|
+
expect(::Kernel).to receive(:sleep).with(0)
|
436
|
+
|
437
|
+
client.get(uri)
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|