prof 0.29.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LEGAL +13 -0
- data/lib/prof/cloud_foundry.rb +145 -0
- data/lib/prof/environment/cloud_foundry.rb +150 -0
- data/lib/prof/environment/pcf_drinks.rb +156 -0
- data/lib/prof/environment_manager.rb +93 -0
- data/lib/prof/external_spec/helpers/capybara.rb +58 -0
- data/lib/prof/external_spec/helpers/debug.rb +53 -0
- data/lib/prof/external_spec/helpers/file_helper.rb +35 -0
- data/lib/prof/external_spec/helpers/product_path.rb +23 -0
- data/lib/prof/external_spec/shared_examples/deployment.rb +23 -0
- data/lib/prof/external_spec/shared_examples/ops_manager_upgrade.rb +32 -0
- data/lib/prof/external_spec/shared_examples/service.rb +105 -0
- data/lib/prof/external_spec/shared_examples/service_broker.rb +25 -0
- data/lib/prof/external_spec/spec_helper.rb +35 -0
- data/lib/prof/marketplace_service.rb +20 -0
- data/lib/prof/matchers/metadata.rb +180 -0
- data/lib/prof/matchers/only_support_ssl_with_cipher_set.rb +102 -0
- data/lib/prof/matchers/ssl.rb +67 -0
- data/lib/prof/matchers/tile_configuration.rb +47 -0
- data/lib/prof/ops_manager/rails_500_error.rb +15 -0
- data/lib/prof/ops_manager/web_app_internals/page/checkbox_field.rb +38 -0
- data/lib/prof/ops_manager/web_app_internals/page/click_field.rb +40 -0
- data/lib/prof/ops_manager/web_app_internals/page/dashboard.rb +100 -0
- data/lib/prof/ops_manager/web_app_internals/page/flash_message.rb +53 -0
- data/lib/prof/ops_manager/web_app_internals/page/form.rb +130 -0
- data/lib/prof/ops_manager/web_app_internals/page/form_error.rb +43 -0
- data/lib/prof/ops_manager/web_app_internals/page/form_field.rb +51 -0
- data/lib/prof/ops_manager/web_app_internals/page/form_fields.rb +35 -0
- data/lib/prof/ops_manager/web_app_internals/page/installation_progress.rb +149 -0
- data/lib/prof/ops_manager/web_app_internals/page/login.rb +53 -0
- data/lib/prof/ops_manager/web_app_internals/page/modal.rb +85 -0
- data/lib/prof/ops_manager/web_app_internals/page/rails_500.rb +41 -0
- data/lib/prof/ops_manager/web_app_internals/page/select_field.rb +27 -0
- data/lib/prof/ops_manager/web_app_internals/page/tile_settings.rb +105 -0
- data/lib/prof/ops_manager/web_app_internals.rb +63 -0
- data/lib/prof/ops_manager.rb +141 -0
- data/lib/prof/ops_manager_log_fetcher.rb +30 -0
- data/lib/prof/product.rb +59 -0
- data/lib/prof/pushed_test_app.rb +40 -0
- data/lib/prof/service_instance.rb +21 -0
- data/lib/prof/ssh_gateway.rb +104 -0
- data/lib/prof/ssl/check.rb +79 -0
- data/lib/prof/ssl/cipher_set.rb +56 -0
- data/lib/prof/ssl/result.rb +27 -0
- data/lib/prof/ssl/results.rb +74 -0
- data/lib/prof/test_app.rb +19 -0
- data/lib/prof/tile.rb +25 -0
- data/lib/prof/uaa_client.rb +66 -0
- data/lib/prof/version.rb +13 -0
- metadata +403 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'json'
|
12
|
+
|
13
|
+
module Prof
|
14
|
+
module ExternalSpec
|
15
|
+
module Helpers
|
16
|
+
module FileHelper
|
17
|
+
def root_path
|
18
|
+
defined?(ROOT_PATH) ? ROOT_PATH : Dir.pwd
|
19
|
+
end
|
20
|
+
|
21
|
+
def file_path(relative_to_root)
|
22
|
+
File.expand_path(relative_to_root, root_path)
|
23
|
+
end
|
24
|
+
|
25
|
+
def json_contents(relative_to_root)
|
26
|
+
JSON.parse(file_contents(relative_to_root), symbolize_names: true)
|
27
|
+
end
|
28
|
+
|
29
|
+
def file_contents(relative_to_root)
|
30
|
+
File.read(file_path(relative_to_root))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
module Prof
|
12
|
+
module ExternalSpec
|
13
|
+
module Helpers
|
14
|
+
module ProductPath
|
15
|
+
def product_path
|
16
|
+
artifact_path = ENV.fetch('TEMPEST_ARTIFACT_PATH') { raise 'Must set TEMPEST_ARTIFACT_PATH environment variable' }
|
17
|
+
fail "Can't find artifact at #{artifact_path}" unless File.exist?(artifact_path)
|
18
|
+
artifact_path
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
shared_examples_for 'a deployment which allows log access via bosh' do
|
12
|
+
it 'allows log access via bosh' do
|
13
|
+
log_files_by_job.each_pair do |job_name, log_files|
|
14
|
+
expect(bosh_director.job_logfiles(job_name)).to include(*log_files)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
shared_examples_for 'a deployment' do
|
20
|
+
describe 'deployment' do
|
21
|
+
it_behaves_like 'a deployment which allows log access via bosh'
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
shared_examples_for 'upgradable product' do
|
12
|
+
it 'persists data across product upgrades' do
|
13
|
+
cloud_foundry.push_app_and_bind_with_service(test_app, old_service) do |pushed_app|
|
14
|
+
puts 'Inserting test data into instance...'
|
15
|
+
pushed_app.write('test_key', 'test_value')
|
16
|
+
expect(pushed_app.read('test_key')).to eq('test_value')
|
17
|
+
|
18
|
+
puts "Uploading #{new_product}..."
|
19
|
+
ops_manager.upload_product(new_product)
|
20
|
+
|
21
|
+
puts "Upgrading to #{new_product}..."
|
22
|
+
ops_manager.upgrade_product(new_product)
|
23
|
+
|
24
|
+
puts "Deploying #{new_product}..."
|
25
|
+
ops_manager.apply_changes
|
26
|
+
|
27
|
+
puts 'Retreiving test data from instance...'
|
28
|
+
expect(pushed_app.read('test_key')).to eq('test_value')
|
29
|
+
puts 'The data survived! :)'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
shared_examples_for 'a basic service' do
|
12
|
+
it 'allows a user to use the service' do
|
13
|
+
cloud_foundry.push_app_and_bind_with_service(test_app, service) do |pushed_app|
|
14
|
+
pushed_app.write('test_key', 'test_value')
|
15
|
+
expect(pushed_app.read('test_key')).to eq('test_value')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
shared_examples_for 'a service that has distinct instances' do
|
21
|
+
it 'has distinct instances' do
|
22
|
+
service_broker.provision_and_bind(service.name, service.plan) do |binding_1|
|
23
|
+
service_client_1 = service_client_builder(binding_1)
|
24
|
+
service_client_1.write('test_key', 'test_value')
|
25
|
+
|
26
|
+
service_broker.provision_and_bind(service.name, service.plan) do |binding_2|
|
27
|
+
service_client_2 = service_client_builder(binding_2)
|
28
|
+
service_client_2.write('test_key', 'another_test_value')
|
29
|
+
expect(service_client_1.read('test_key')).to eq('test_value')
|
30
|
+
expect(service_client_2.read('test_key')).to eq('another_test_value')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
shared_examples_for 'a service that can be shared by multiple applications' do
|
37
|
+
it 'allows two applications to share the same instance' do
|
38
|
+
service_broker.provision_instance(service.name, service.plan) do |service_instance|
|
39
|
+
service_broker.bind_instance(service_instance) do |binding_1|
|
40
|
+
service_client_1 = service_client_builder(binding_1)
|
41
|
+
service_client_1.write('shared_test_key', 'test_value')
|
42
|
+
expect(service_client_1.read('shared_test_key')).to eq('test_value')
|
43
|
+
|
44
|
+
service_broker.bind_instance(service_instance) do |binding_2|
|
45
|
+
service_client_2 = service_client_builder(binding_2)
|
46
|
+
expect(service_client_2.read('shared_test_key')).to eq('test_value')
|
47
|
+
end
|
48
|
+
|
49
|
+
expect(service_client_1.read('shared_test_key')).to eq('test_value')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
shared_examples_for 'a service which preserves data across binding and unbinding' do
|
56
|
+
it 'preserves data across binding and unbinding' do
|
57
|
+
service_broker.provision_instance(service.name, service.plan) do |service_instance|
|
58
|
+
service_broker.bind_instance(service_instance) do |binding|
|
59
|
+
service_client_builder(binding).write('unbound_test_key', 'test_value')
|
60
|
+
end
|
61
|
+
|
62
|
+
service_broker.bind_instance(service_instance) do |binding|
|
63
|
+
expect(service_client_builder(binding).read('unbound_test_key')).to eq('test_value')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
shared_examples_for 'a service which preserves data when recreating the broker VM' do
|
70
|
+
it 'preserves data when recreating vms' do
|
71
|
+
service_broker.provision_and_bind(service.name, service.plan) do |binding|
|
72
|
+
service_client = service_client_builder(binding)
|
73
|
+
service_client.write('test_key', 'test_value')
|
74
|
+
expect(service_client.read('test_key')).to eq('test_value')
|
75
|
+
|
76
|
+
bosh_director.recreate_all([environment.bosh_service_broker_job_name])
|
77
|
+
|
78
|
+
expect(service_client.read('test_key')).to eq('test_value')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
shared_examples_for 'a persistent cloud foundry service' do
|
84
|
+
describe 'a persistent cloud foundry service' do
|
85
|
+
it_behaves_like 'a service that has distinct instances'
|
86
|
+
it_behaves_like 'a service that can be shared by multiple applications'
|
87
|
+
it_behaves_like 'a service which preserves data across binding and unbinding'
|
88
|
+
if !method_defined?(:manually_drain)
|
89
|
+
it_behaves_like 'a service which preserves data when recreating the broker VM'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# DEPRECATED
|
95
|
+
shared_examples_for 'a service which preserves data when recreating VMs' do
|
96
|
+
it do
|
97
|
+
pending "switch shared example to 'a service which preserves data when recreating the broker VM'"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
shared_examples_for 'a multi-tenant service' do
|
102
|
+
it do
|
103
|
+
pending "switch shared example to 'a persistent cloud foundry service'"
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
shared_examples_for 'a service broker which logs to syslog' do
|
12
|
+
it 'logs to syslog' do
|
13
|
+
broker_ip = broker_host || environment.service_broker.url.host
|
14
|
+
nginx_syslog_line_count = Integer(ssh_gateway.execute_on(broker_ip, "grep -c #{syslog_tag} /var/log/syslog"))
|
15
|
+
expect(nginx_syslog_line_count).to be > 0
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
shared_examples_for 'a service broker' do
|
20
|
+
let(:syslog_tag) { "Cf#{environment.service_broker_name.capitalize}BrokerNginxAccess" }
|
21
|
+
|
22
|
+
describe 'service broker' do
|
23
|
+
it_behaves_like 'a service broker which logs to syslog'
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'prof/external_spec/helpers/capybara'
|
12
|
+
require 'prof/external_spec/helpers/debug'
|
13
|
+
require 'prof/external_spec/helpers/file_helper'
|
14
|
+
require 'prof/external_spec/helpers/product_path'
|
15
|
+
|
16
|
+
require 'rspec_junit_formatter'
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.include Prof::ExternalSpec::Helpers::Capybara
|
20
|
+
config.include Prof::ExternalSpec::Helpers::Debug
|
21
|
+
config.include Prof::ExternalSpec::Helpers::FileHelper
|
22
|
+
config.include Prof::ExternalSpec::Helpers::ProductPath
|
23
|
+
|
24
|
+
config.add_formatter RSpecJUnitFormatter, 'rspec.xml'
|
25
|
+
|
26
|
+
config.full_backtrace = true
|
27
|
+
|
28
|
+
config.before(:all) do
|
29
|
+
setup_browser(:webkit)
|
30
|
+
end
|
31
|
+
|
32
|
+
config.after(:each) do |example|
|
33
|
+
save_exception_output(example)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
module Prof
|
12
|
+
class MarketplaceService
|
13
|
+
def initialize(name:, plan:)
|
14
|
+
@name = name
|
15
|
+
@plan = plan
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :name, :plan
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
RSpec::Matchers.define :have_metadata_version do |version|
|
12
|
+
actual_version = 'NOT_SET'
|
13
|
+
match do |actual|
|
14
|
+
actual_version = actual['metadata_version']
|
15
|
+
actual_version == version.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
failure_message do
|
19
|
+
"Metadata version does not match. Expected version #{version}, actual version was #{actual_version}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
RSpec::Matchers.define :have_job_type do |job_name|
|
24
|
+
error_message = ''
|
25
|
+
match do |actual|
|
26
|
+
begin
|
27
|
+
metadata = MetadataWrapper.new(actual)
|
28
|
+
|
29
|
+
job = metadata.job(job_name: job_name)
|
30
|
+
return !!job unless @instance_name || @resource_name || @template_name
|
31
|
+
|
32
|
+
return job.has_template?(template_name: @template_name) if @template_name
|
33
|
+
|
34
|
+
definition = @instance_name ? job.instance_definition(definition_name: @instance_name) : job.resource_definition(definition_name: @resource_name)
|
35
|
+
return !! definition unless @attribute || @constraint
|
36
|
+
|
37
|
+
return definition.has_attribute_with_value?(@attribute, @value) if @attribute
|
38
|
+
|
39
|
+
definition.has_constraint_with_value?(@constraint, @value)
|
40
|
+
rescue MetadataError => error
|
41
|
+
error_message = error.message
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
chain :with_template do |template_name|
|
47
|
+
@template_name = template_name.to_s
|
48
|
+
end
|
49
|
+
|
50
|
+
chain :with_instance_definition do |instance_name|
|
51
|
+
@instance_name = instance_name.to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
chain :with_resource_definition do |resource_name|
|
55
|
+
@resource_name = resource_name.to_s
|
56
|
+
end
|
57
|
+
|
58
|
+
chain :with_constraint do |constraint, value|
|
59
|
+
raise MetadataError.new("Can not use `with_contraint` without `with_instance_definition` or `with_resource_definition`") unless @resource_name || @instance_name
|
60
|
+
@constraint = constraint.to_s
|
61
|
+
@value = value
|
62
|
+
end
|
63
|
+
|
64
|
+
chain :with_attribute_value do |attribute, value|
|
65
|
+
raise MetadataError.new("Can not use `with_attribute_value` without `with_instance_definition` or `with_resource_definition`") unless @resource_name || @instance_name
|
66
|
+
@attribute = attribute.to_s
|
67
|
+
@value = value
|
68
|
+
end
|
69
|
+
|
70
|
+
chain :with_default do |value|
|
71
|
+
raise MetadataError.new("Can not use `with_default` without `with_instance_definition` or `with_resource_definition`") unless @resource_name || @instance_name
|
72
|
+
@attribute = 'default'
|
73
|
+
@value = value
|
74
|
+
end
|
75
|
+
|
76
|
+
failure_message do
|
77
|
+
error_message
|
78
|
+
end
|
79
|
+
|
80
|
+
class MetadataError < StandardError; end
|
81
|
+
|
82
|
+
class MetadataWrapper
|
83
|
+
def initialize(metadata)
|
84
|
+
@metadata = metadata
|
85
|
+
end
|
86
|
+
|
87
|
+
def job(job_name:)
|
88
|
+
job = jobs.find { |j| j['name'] == job_name.to_s }.tap do |j|
|
89
|
+
raise MetadataError.new("Could not find job type: #{job_name} in metadata") unless j
|
90
|
+
end
|
91
|
+
|
92
|
+
MetadataJob.new(job)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
attr_reader :metadata
|
98
|
+
|
99
|
+
def jobs
|
100
|
+
metadata['job_types']
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class MetadataJob
|
105
|
+
def initialize(job)
|
106
|
+
@job = job
|
107
|
+
end
|
108
|
+
|
109
|
+
def has_template?(template_name:)
|
110
|
+
job['job_templates'].include?(template_name).tap do |result|
|
111
|
+
raise MetadataError.new("Could not find template '#{template_name}' for job #{job_name} in metadata") unless result
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def instance_definition(definition_name:)
|
116
|
+
instance_definition = instance_definitions.find { |definition| definition['name'] == definition_name }.tap do |instance|
|
117
|
+
raise MetadataError.new("Could not find instance type: #{definition_name} for job type: #{job_name}") unless instance
|
118
|
+
end
|
119
|
+
MetadataDefinition.new(instance_definition, job_name)
|
120
|
+
end
|
121
|
+
|
122
|
+
def resource_definition(definition_name:)
|
123
|
+
resource_definition = resource_definitions.find { |definition| definition['name'] == definition_name }.tap do |resource|
|
124
|
+
raise MetadataError.new("Could not find resource type: #{definition_name} for job type: #{job_name}") unless resource
|
125
|
+
end
|
126
|
+
MetadataDefinition.new(resource_definition, job_name)
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
attr_reader :job
|
132
|
+
|
133
|
+
def job_name
|
134
|
+
job['name']
|
135
|
+
end
|
136
|
+
|
137
|
+
def instance_definitions
|
138
|
+
job['instance_definitions']
|
139
|
+
end
|
140
|
+
|
141
|
+
def resource_definitions
|
142
|
+
job['resource_definitions']
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
class MetadataDefinition
|
148
|
+
def initialize(definition, job_name)
|
149
|
+
@job_name = job_name
|
150
|
+
@definition = definition
|
151
|
+
end
|
152
|
+
|
153
|
+
def has_attribute_with_value?(attribute, value)
|
154
|
+
actual_value = definition.fetch(attribute) do
|
155
|
+
raise MetadataError.new("Could not find attribute #{attribute} for definition #{definition_name} in job type: #{job_name}")
|
156
|
+
end
|
157
|
+
|
158
|
+
return true if actual_value == value
|
159
|
+
raise MetadataError.new("Expected attribute #{attribute} value to be #{value} but it was #{actual_value} for definition #{definition_name} in job type: #{job_name}")
|
160
|
+
end
|
161
|
+
|
162
|
+
def has_constraint_with_value?(constraint, value)
|
163
|
+
actual_value = definition['constraints'].fetch(constraint) do
|
164
|
+
raise MetadataError.new("Could not find constraint #{constraint} for definition #{definition_name} in job type: #{job_name}")
|
165
|
+
end
|
166
|
+
|
167
|
+
return true if actual_value == value
|
168
|
+
raise MetadataError.new("Expected constraint #{constraint} value to be #{value} but it was #{actual_value} for definition #{definition_name} in job type: #{job_name}")
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
attr_reader :definition, :job_name
|
174
|
+
|
175
|
+
def definition_name
|
176
|
+
definition['name']
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'ostruct'
|
12
|
+
|
13
|
+
require 'prof/ssl/check'
|
14
|
+
|
15
|
+
require 'rspec/matchers/pretty'
|
16
|
+
|
17
|
+
module Prof
|
18
|
+
module Matchers
|
19
|
+
def only_support_ssl_with_cipher_set(*args)
|
20
|
+
OnlySupportSslWithCipherSet.new(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Current problems
|
24
|
+
# 1. The OSX openssl library may not support all of the ciphers that need to be tested for a cipher suite
|
25
|
+
# 2. Some of the ciphers are actually expressions (kEDH+AESGCM) these need to be expanded to the ciphers they represent
|
26
|
+
|
27
|
+
class OnlySupportSslWithCipherSet
|
28
|
+
|
29
|
+
include RSpec::Matchers::Pretty
|
30
|
+
|
31
|
+
def initialize(cipher_set)
|
32
|
+
@cipher_set = cipher_set
|
33
|
+
end
|
34
|
+
|
35
|
+
def matches?(https_url)
|
36
|
+
@https_url = https_url
|
37
|
+
@results = ssl_results
|
38
|
+
@http_enabled = http_connection_accepted?
|
39
|
+
results.supports_cipher_set?(cipher_set) && !@http_enabled
|
40
|
+
end
|
41
|
+
|
42
|
+
def with_proxy(proxy)
|
43
|
+
@proxy = proxy
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def failure_message
|
48
|
+
[
|
49
|
+
("The server is missing support for#{to_sentence server_missing_supported_ciphers}" if server_missing_supported_ciphers.any?),
|
50
|
+
("The server supports#{to_sentence server_extra_ciphers} when it should not" if server_extra_ciphers.any?),
|
51
|
+
("The server is missing support for#{to_sentence server_missing_supported_protocols}" if server_missing_supported_protocols.any?),
|
52
|
+
("The server supports#{to_sentence server_extra_protocols} when it should not" if server_extra_protocols.any?),
|
53
|
+
("The server supports HTTP when it should not" if http_enabled)
|
54
|
+
].compact.join("\n")
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
attr_reader :cipher_set, :results, :https_url, :http_enabled
|
60
|
+
|
61
|
+
def proxy
|
62
|
+
@proxy ||= OpenStruct.new(:http_host => nil, :http_address => nil)
|
63
|
+
end
|
64
|
+
|
65
|
+
def server_missing_supported_ciphers
|
66
|
+
cipher_set.supported_ciphers - results.supported_ciphers
|
67
|
+
end
|
68
|
+
|
69
|
+
def server_extra_ciphers
|
70
|
+
results.supported_ciphers - cipher_set.supported_ciphers
|
71
|
+
end
|
72
|
+
|
73
|
+
def server_missing_supported_protocols
|
74
|
+
cipher_set.supported_protocols - results.supported_protocols
|
75
|
+
end
|
76
|
+
|
77
|
+
def server_extra_protocols
|
78
|
+
results.supported_protocols - cipher_set.supported_protocols
|
79
|
+
end
|
80
|
+
|
81
|
+
def http_connection_accepted?
|
82
|
+
begin
|
83
|
+
response = Net::HTTP.new(http_uri.host, http_uri.port, proxy.http_host, proxy.http_port).get('/')
|
84
|
+
!response.instance_of?(Net::HTTPGatewayTimeOut)
|
85
|
+
rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT
|
86
|
+
false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def http_uri
|
91
|
+
http_uri = URI(https_url)
|
92
|
+
http_uri.scheme = 'http'
|
93
|
+
http_uri.port = 80
|
94
|
+
http_uri
|
95
|
+
end
|
96
|
+
|
97
|
+
def ssl_results
|
98
|
+
Prof::SSL::Check.new(https_url, @proxy).results
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'prof/ssl/check'
|
12
|
+
|
13
|
+
require 'rspec/expectations'
|
14
|
+
|
15
|
+
RSpec::Matchers.define :support_ssl_protocol do |expected|
|
16
|
+
match do |url|
|
17
|
+
actual = Prof::SSL::Check.new(url, @proxy).results.supported_protocols
|
18
|
+
actual.include?(expected)
|
19
|
+
end
|
20
|
+
|
21
|
+
chain :with_proxy do |proxy|
|
22
|
+
@proxy = proxy
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
RSpec::Matchers.define :support_ssl_protocols do |expected|
|
27
|
+
match do |url|
|
28
|
+
@results = ssl_results(url)
|
29
|
+
|
30
|
+
expected.all? do |protocol, is_supported|
|
31
|
+
@results.supported_protocols.include?(protocol) == is_supported
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
chain :with_proxy do |proxy|
|
36
|
+
@proxy = proxy
|
37
|
+
end
|
38
|
+
|
39
|
+
failure_message do |_|
|
40
|
+
unexpectedly_supported = expected.reject { |_, supported| supported }.keys & @results.supported_protocols
|
41
|
+
unexpectedly_unsupported = expected.select { |_, supported| supported }.keys & @results.unsupported_protocols
|
42
|
+
|
43
|
+
"expected SSL support: #{expected.inspect}, but" + to_sentence(
|
44
|
+
[("#{unexpectedly_supported} unexpectedly supported" if unexpectedly_supported.any?),
|
45
|
+
("#{unexpectedly_unsupported} unexpectedly unsupported" if unexpectedly_unsupported.any?)].compact
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
attr_reader :ssh_gateway
|
52
|
+
|
53
|
+
def ssl_results(url)
|
54
|
+
if ssh_gateway
|
55
|
+
uri = URI.parse(url)
|
56
|
+
ssh_gateway.with_port_forwarded_to(uri.host, uri.port) do |forwarded_port|
|
57
|
+
ssl_check_url("https://localhost:#{forwarded_port}")
|
58
|
+
end
|
59
|
+
else
|
60
|
+
ssl_check_url(url)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def ssl_check_url(url)
|
65
|
+
Prof::SSL::Check.new(url, @proxy).results
|
66
|
+
end
|
67
|
+
end
|