prof 0.29.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|