prof 0.29.3

Sign up to get free protection for your applications and to get access to all the features.
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