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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/LEGAL +13 -0
  3. data/lib/prof/cloud_foundry.rb +145 -0
  4. data/lib/prof/environment/cloud_foundry.rb +150 -0
  5. data/lib/prof/environment/pcf_drinks.rb +156 -0
  6. data/lib/prof/environment_manager.rb +93 -0
  7. data/lib/prof/external_spec/helpers/capybara.rb +58 -0
  8. data/lib/prof/external_spec/helpers/debug.rb +53 -0
  9. data/lib/prof/external_spec/helpers/file_helper.rb +35 -0
  10. data/lib/prof/external_spec/helpers/product_path.rb +23 -0
  11. data/lib/prof/external_spec/shared_examples/deployment.rb +23 -0
  12. data/lib/prof/external_spec/shared_examples/ops_manager_upgrade.rb +32 -0
  13. data/lib/prof/external_spec/shared_examples/service.rb +105 -0
  14. data/lib/prof/external_spec/shared_examples/service_broker.rb +25 -0
  15. data/lib/prof/external_spec/spec_helper.rb +35 -0
  16. data/lib/prof/marketplace_service.rb +20 -0
  17. data/lib/prof/matchers/metadata.rb +180 -0
  18. data/lib/prof/matchers/only_support_ssl_with_cipher_set.rb +102 -0
  19. data/lib/prof/matchers/ssl.rb +67 -0
  20. data/lib/prof/matchers/tile_configuration.rb +47 -0
  21. data/lib/prof/ops_manager/rails_500_error.rb +15 -0
  22. data/lib/prof/ops_manager/web_app_internals/page/checkbox_field.rb +38 -0
  23. data/lib/prof/ops_manager/web_app_internals/page/click_field.rb +40 -0
  24. data/lib/prof/ops_manager/web_app_internals/page/dashboard.rb +100 -0
  25. data/lib/prof/ops_manager/web_app_internals/page/flash_message.rb +53 -0
  26. data/lib/prof/ops_manager/web_app_internals/page/form.rb +130 -0
  27. data/lib/prof/ops_manager/web_app_internals/page/form_error.rb +43 -0
  28. data/lib/prof/ops_manager/web_app_internals/page/form_field.rb +51 -0
  29. data/lib/prof/ops_manager/web_app_internals/page/form_fields.rb +35 -0
  30. data/lib/prof/ops_manager/web_app_internals/page/installation_progress.rb +149 -0
  31. data/lib/prof/ops_manager/web_app_internals/page/login.rb +53 -0
  32. data/lib/prof/ops_manager/web_app_internals/page/modal.rb +85 -0
  33. data/lib/prof/ops_manager/web_app_internals/page/rails_500.rb +41 -0
  34. data/lib/prof/ops_manager/web_app_internals/page/select_field.rb +27 -0
  35. data/lib/prof/ops_manager/web_app_internals/page/tile_settings.rb +105 -0
  36. data/lib/prof/ops_manager/web_app_internals.rb +63 -0
  37. data/lib/prof/ops_manager.rb +141 -0
  38. data/lib/prof/ops_manager_log_fetcher.rb +30 -0
  39. data/lib/prof/product.rb +59 -0
  40. data/lib/prof/pushed_test_app.rb +40 -0
  41. data/lib/prof/service_instance.rb +21 -0
  42. data/lib/prof/ssh_gateway.rb +104 -0
  43. data/lib/prof/ssl/check.rb +79 -0
  44. data/lib/prof/ssl/cipher_set.rb +56 -0
  45. data/lib/prof/ssl/result.rb +27 -0
  46. data/lib/prof/ssl/results.rb +74 -0
  47. data/lib/prof/test_app.rb +19 -0
  48. data/lib/prof/tile.rb +25 -0
  49. data/lib/prof/uaa_client.rb +66 -0
  50. data/lib/prof/version.rb +13 -0
  51. 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