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,149 @@
|
|
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/ops_manager/web_app_internals/page/modal'
|
12
|
+
require 'prof/ops_manager/web_app_internals/page/flash_message'
|
13
|
+
require 'prof/ops_manager/web_app_internals/page/rails_500'
|
14
|
+
|
15
|
+
require 'nokogiri'
|
16
|
+
|
17
|
+
module Prof
|
18
|
+
class OpsManager
|
19
|
+
|
20
|
+
class InstallationError < StandardError; end
|
21
|
+
|
22
|
+
class WebAppInternals
|
23
|
+
module Page
|
24
|
+
class InstallationProgress
|
25
|
+
MAX_INSTALL_STEP_SECONDS = 60 * 45
|
26
|
+
|
27
|
+
class TimeoutWaitingForStep < InstallationError; end
|
28
|
+
|
29
|
+
def initialize(page:, output: STDOUT)
|
30
|
+
@page = page
|
31
|
+
@output = output
|
32
|
+
|
33
|
+
if flash_message.icmp_error?
|
34
|
+
flash_message.ignore_warnings
|
35
|
+
end
|
36
|
+
|
37
|
+
raise InstallationError, flash_message.text if flash_message.error?
|
38
|
+
end
|
39
|
+
|
40
|
+
def install_successful?
|
41
|
+
install_steps.each do |step|
|
42
|
+
wait_until_completed step
|
43
|
+
end
|
44
|
+
|
45
|
+
if modal.success?
|
46
|
+
modal.confirm
|
47
|
+
return true
|
48
|
+
else
|
49
|
+
output.puts modal.message
|
50
|
+
modal.confirm
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
attr_reader :page, :output
|
58
|
+
|
59
|
+
def log(dt, _step)
|
60
|
+
elapsed_time = sprintf('%02i:%02i:%02i', dt / 3600, dt / 60, dt % 60)
|
61
|
+
output.puts " done. [#{elapsed_time}]"
|
62
|
+
end
|
63
|
+
|
64
|
+
def wait_until_completed(step)
|
65
|
+
start = Time.now
|
66
|
+
output.print "Running step [#{step.index}/#{step.total}]: #{step}..."
|
67
|
+
page.document.synchronize(MAX_INSTALL_STEP_SECONDS, errors: [TimeoutWaitingForStep]) do
|
68
|
+
raise TimeoutWaitingForStep, "Timed out after #{MAX_INSTALL_STEP_SECONDS} seconds waiting for #{step.name} to finish.\n\n#{verbose_output}" unless step.finished?
|
69
|
+
end
|
70
|
+
duration = Time.now - start
|
71
|
+
|
72
|
+
log(duration, step)
|
73
|
+
end
|
74
|
+
|
75
|
+
def modal
|
76
|
+
Modal.new(page: page)
|
77
|
+
end
|
78
|
+
|
79
|
+
def flash_message
|
80
|
+
FlashMessage.new(page: page)
|
81
|
+
end
|
82
|
+
|
83
|
+
def install_steps
|
84
|
+
@install_steps ||= begin
|
85
|
+
step_nodes = page.all('#install-steps .step')
|
86
|
+
raise rails_500.error if rails_500?
|
87
|
+
raise 'No install steps found' if step_nodes.empty?
|
88
|
+
|
89
|
+
step_nodes.map.with_index(1) do |node, index|
|
90
|
+
InstallStep.new(node: node, page: page, index: index, total: step_nodes.size)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def verbose_output
|
96
|
+
Nokogiri::HTML(page.html).css('.output').text
|
97
|
+
end
|
98
|
+
|
99
|
+
def rails_500?
|
100
|
+
rails_500.matches?
|
101
|
+
end
|
102
|
+
|
103
|
+
def rails_500
|
104
|
+
Rails500.new(page: page)
|
105
|
+
end
|
106
|
+
|
107
|
+
class InstallStep
|
108
|
+
class InstallStepNotPresent < InstallationError; end
|
109
|
+
|
110
|
+
def initialize(node:, page:, index: index, total: total)
|
111
|
+
@id = node['data-step-id']
|
112
|
+
@page = page
|
113
|
+
@index = index
|
114
|
+
@total = total
|
115
|
+
end
|
116
|
+
|
117
|
+
def name
|
118
|
+
node.text
|
119
|
+
end
|
120
|
+
|
121
|
+
def finished?
|
122
|
+
state == 'finished'
|
123
|
+
end
|
124
|
+
|
125
|
+
def state
|
126
|
+
node['data-step-state']
|
127
|
+
end
|
128
|
+
|
129
|
+
def to_s
|
130
|
+
name
|
131
|
+
end
|
132
|
+
|
133
|
+
attr_reader :index, :total
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
attr_reader :page, :id
|
138
|
+
|
139
|
+
def node
|
140
|
+
page.find("[data-step-id='#{id}']")
|
141
|
+
rescue Capybara::ElementNotFound
|
142
|
+
raise InstallStepNotPresent, "The install step '#{id}'' is no longer on the page - has Ops Manager crashed?"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,53 @@
|
|
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 'uri'
|
12
|
+
|
13
|
+
require 'prof/ops_manager/web_app_internals/page/dashboard'
|
14
|
+
|
15
|
+
module Prof
|
16
|
+
class OpsManager
|
17
|
+
class WebAppInternals
|
18
|
+
module Page
|
19
|
+
class Login
|
20
|
+
def initialize(page:, url:, username:, password:)
|
21
|
+
@page = page
|
22
|
+
@url = url
|
23
|
+
@username = username
|
24
|
+
@password = password
|
25
|
+
|
26
|
+
resp = page.visit login_url
|
27
|
+
|
28
|
+
if resp['status'] == 'fail'
|
29
|
+
raise StandardError.new("Failed to fetch url: #{login_url}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def login
|
34
|
+
puts "Logging into tempest at #{login_url}"
|
35
|
+
page.fill_in 'login[user_name]', with: username
|
36
|
+
page.fill_in 'login[password]', with: password
|
37
|
+
page.click_on 'login-action'
|
38
|
+
|
39
|
+
Dashboard.new(page: page)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :page, :url, :username, :password
|
45
|
+
|
46
|
+
def login_url
|
47
|
+
URI.join(url, 'login')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,85 @@
|
|
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 OpsManager
|
13
|
+
class WebAppInternals
|
14
|
+
module Page
|
15
|
+
class Modal
|
16
|
+
|
17
|
+
ModalStillPresent = Class.new(StandardError)
|
18
|
+
|
19
|
+
def initialize(page:)
|
20
|
+
@page = page
|
21
|
+
end
|
22
|
+
|
23
|
+
def confirm
|
24
|
+
# We have spent a long time trying to debug unreliable clicking of confirm
|
25
|
+
# in both Selenium and Poltegeist. The box is animated which we think is causing
|
26
|
+
# issues with where the browser is clicking. A sleep will resolve this, and has
|
27
|
+
# lowest complexity.
|
28
|
+
Kernel.sleep(3)
|
29
|
+
|
30
|
+
confirm_button.click
|
31
|
+
|
32
|
+
await_modal_disappearance!
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
def success?
|
37
|
+
/success/ =~ modal['id']
|
38
|
+
end
|
39
|
+
|
40
|
+
def failure?
|
41
|
+
/failure/ =~ modal['id']
|
42
|
+
end
|
43
|
+
|
44
|
+
def message
|
45
|
+
modal.find('.modal-body').text
|
46
|
+
end
|
47
|
+
|
48
|
+
def present?
|
49
|
+
page.all(modal_css_selector).any? || page.all('.modal-backdrop').any?
|
50
|
+
rescue Capybara::Poltergeist::ObsoleteNode
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
attr_reader :page
|
57
|
+
|
58
|
+
def await_modal_disappearance!
|
59
|
+
# Modal takes a long time to disappear as the next page can take a
|
60
|
+
# long time to load
|
61
|
+
page.document.synchronize(60, errors: [ModalStillPresent]) do
|
62
|
+
raise ModalStillPresent if present?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def modal_css_selector
|
67
|
+
'.modal.in'
|
68
|
+
end
|
69
|
+
|
70
|
+
def modal
|
71
|
+
page.find(modal_css_selector)
|
72
|
+
end
|
73
|
+
|
74
|
+
def confirm_button
|
75
|
+
modal.find('.modal-footer .btn-primary')
|
76
|
+
end
|
77
|
+
|
78
|
+
def poltergeist?
|
79
|
+
page.driver.class.to_s == 'Capybara::Poltergeist::Driver'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,41 @@
|
|
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/ops_manager/rails_500_error'
|
12
|
+
|
13
|
+
module Prof
|
14
|
+
class OpsManager
|
15
|
+
class WebAppInternals
|
16
|
+
module Page
|
17
|
+
class Rails500
|
18
|
+
def initialize(page:)
|
19
|
+
@page = page
|
20
|
+
end
|
21
|
+
|
22
|
+
def matches?
|
23
|
+
page.all('.error-page.rails-env-production').any?
|
24
|
+
end
|
25
|
+
|
26
|
+
def error
|
27
|
+
Rails500Error.new(message)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :page
|
33
|
+
|
34
|
+
def message
|
35
|
+
page.all('.error').map(&:text).join("\n")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,27 @@
|
|
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/ops_manager/web_app_internals/page/form_field'
|
12
|
+
|
13
|
+
module Prof
|
14
|
+
class OpsManager
|
15
|
+
class WebAppInternals
|
16
|
+
module Page
|
17
|
+
class SelectField < FormField
|
18
|
+
private
|
19
|
+
|
20
|
+
def set_value(field)
|
21
|
+
field.find(:option, value).select_option
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
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
|
+
require 'prof/ops_manager/web_app_internals/page/dashboard'
|
12
|
+
require 'prof/ops_manager/web_app_internals/page/form'
|
13
|
+
require 'uri'
|
14
|
+
|
15
|
+
module Prof
|
16
|
+
class OpsManager
|
17
|
+
class WebAppInternals
|
18
|
+
module Page
|
19
|
+
class TileSettings
|
20
|
+
def initialize(opts = {})
|
21
|
+
@page = opts.fetch(:page)
|
22
|
+
|
23
|
+
check_on_settings!
|
24
|
+
end
|
25
|
+
|
26
|
+
def setting_names
|
27
|
+
page.all('.sidebar li').map(&:text)
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_field_value(setting_name, field_name, field_value)
|
31
|
+
sidebar(setting_name)
|
32
|
+
update(field_name => field_value)
|
33
|
+
end
|
34
|
+
|
35
|
+
def settings_for(setting_name)
|
36
|
+
sidebar(setting_name)
|
37
|
+
|
38
|
+
config = {}
|
39
|
+
page.all(".content #{fields_selector}").each do |input|
|
40
|
+
config[input['name']] = if config[input['name']]
|
41
|
+
[input.value] + Array(config[input['name']])
|
42
|
+
else
|
43
|
+
input.value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
config
|
47
|
+
end
|
48
|
+
|
49
|
+
def configure(configuration)
|
50
|
+
configuration.each { |sidebar_name, config| sidebar(sidebar_name).update(config) }
|
51
|
+
return_to_dashboard
|
52
|
+
end
|
53
|
+
|
54
|
+
def update(config = {}, &block)
|
55
|
+
form.update config, &block
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def sidebar(next_settings_name, &_block)
|
60
|
+
return self if next_settings_name == current_setting_name
|
61
|
+
|
62
|
+
page.find('li a', text: /^\s*#{next_settings_name}\s*$/).click
|
63
|
+
|
64
|
+
if block_given?
|
65
|
+
yield page
|
66
|
+
return_to_dashboard
|
67
|
+
else
|
68
|
+
TileSettings.new(page: page)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def return_to_dashboard
|
73
|
+
page.click_on 'Installation Dashboard'
|
74
|
+
Dashboard.new(page: page)
|
75
|
+
end
|
76
|
+
|
77
|
+
def current_uri
|
78
|
+
URI(page.current_url)
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
attr_reader :page
|
84
|
+
|
85
|
+
def current_setting_name
|
86
|
+
page.find('.sidebar li.active').text
|
87
|
+
end
|
88
|
+
|
89
|
+
def fields_selector
|
90
|
+
'input[type="text"],textarea,input[type="checkbox"][checked],input[type="radio"][selected],select'
|
91
|
+
end
|
92
|
+
|
93
|
+
def form
|
94
|
+
@form ||= Form.new(page: page, form_element: page.find('.content form'))
|
95
|
+
end
|
96
|
+
|
97
|
+
def check_on_settings!
|
98
|
+
return if current_uri.path.end_with?('edit')
|
99
|
+
raise "Expected to be on a settings page, but am on #{page.current_url}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,63 @@
|
|
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/ops_manager/web_app_internals/page/dashboard'
|
12
|
+
require 'prof/ops_manager/web_app_internals/page/flash_message'
|
13
|
+
require 'prof/ops_manager/web_app_internals/page/login'
|
14
|
+
require 'prof/tile'
|
15
|
+
|
16
|
+
module Prof
|
17
|
+
class OpsManager
|
18
|
+
class WebAppInternals
|
19
|
+
def initialize(dashboard, opsmanager_client, page, url)
|
20
|
+
@dashboard = dashboard
|
21
|
+
@page = page
|
22
|
+
@opsmanager_client = opsmanager_client
|
23
|
+
@url = url
|
24
|
+
end
|
25
|
+
|
26
|
+
def apply_changes
|
27
|
+
visit
|
28
|
+
check_nothing_in_progress!
|
29
|
+
return unless dashboard.pending_changes?
|
30
|
+
raise 'Could not apply changes' unless dashboard.apply_changes.install_successful?
|
31
|
+
end
|
32
|
+
|
33
|
+
def tile_uninstall(tile)
|
34
|
+
return unless opsmanager_client.product_type_installed?(tile)
|
35
|
+
visit
|
36
|
+
check_nothing_in_progress!
|
37
|
+
raise 'Uninstall was not successful' unless dashboard.tile_uninstall(tile)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_reader :dashboard, :page, :opsmanager_client, :url
|
43
|
+
|
44
|
+
def visit
|
45
|
+
page.visit(url) unless page.current_url == url
|
46
|
+
end
|
47
|
+
|
48
|
+
def flash_message
|
49
|
+
Page::FlashMessage.new(page: page)
|
50
|
+
end
|
51
|
+
|
52
|
+
def check_nothing_in_progress!
|
53
|
+
raise 'Installation already in progress' if flash_message.installation_in_progress?
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_cf_tile_if_needed
|
57
|
+
unless page.has_selector?("a[id='show-cf-configure-action']")
|
58
|
+
page.find('#add-cf', visible: false).trigger('click')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,141 @@
|
|
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 'capybara'
|
12
|
+
require 'opsmanager_client/client'
|
13
|
+
require 'forwardable'
|
14
|
+
|
15
|
+
require 'prof/ops_manager/web_app_internals'
|
16
|
+
require 'prof/ops_manager/rails_500_error'
|
17
|
+
require 'prof/tile'
|
18
|
+
require 'json'
|
19
|
+
|
20
|
+
module Prof
|
21
|
+
class OpsManager
|
22
|
+
extend Forwardable
|
23
|
+
|
24
|
+
def initialize(url:, username:, password:, page: default_capybara_session, log_fetcher: null_log_fetcher)
|
25
|
+
@url = url
|
26
|
+
@username = username
|
27
|
+
@password = password
|
28
|
+
@page = page
|
29
|
+
@log_fetcher = log_fetcher
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :url
|
33
|
+
|
34
|
+
def_delegators :web_app_internals,
|
35
|
+
:apply_changes
|
36
|
+
|
37
|
+
def_delegators :opsmanager_client,
|
38
|
+
:cc_client_credentials,
|
39
|
+
:cf_admin_credentials,
|
40
|
+
:cf_installed?,
|
41
|
+
:delete_unused_products,
|
42
|
+
:system_domain,
|
43
|
+
:upgrade_product,
|
44
|
+
:vms_for_job_type
|
45
|
+
|
46
|
+
def_delegators :dashboard,
|
47
|
+
:apply_setting,
|
48
|
+
:tile,
|
49
|
+
:tile_configuration,
|
50
|
+
:tile_configure
|
51
|
+
|
52
|
+
# Add ability to get hold of the capybara session
|
53
|
+
# while transitioning code over to page objects
|
54
|
+
def browser(&_block)
|
55
|
+
yield page, url
|
56
|
+
end
|
57
|
+
|
58
|
+
def uninstall_tile(*tile_or_tiles)
|
59
|
+
tiles = Array(tile_or_tiles).flatten
|
60
|
+
return if tiles.empty?
|
61
|
+
|
62
|
+
tiles.each { |t| web_app_internals.tile_uninstall(t) }
|
63
|
+
web_app_internals.apply_changes
|
64
|
+
end
|
65
|
+
alias_method :uninstall_tiles, :uninstall_tile
|
66
|
+
|
67
|
+
def setup_tile(tile)
|
68
|
+
puts "Adding #{tile.name}"
|
69
|
+
add_tile(tile)
|
70
|
+
|
71
|
+
puts "Installing update"
|
72
|
+
apply_changes
|
73
|
+
rescue Rails500Error => e
|
74
|
+
puts "FAILED: '#{e.message}'\n"
|
75
|
+
logs = log_fetcher.fetch_logs('production.log', 500)
|
76
|
+
raise Rails500Error, "#{e.message}\n---------------\n#{logs}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_tile(tile)
|
80
|
+
opsmanager_client.add_product(tile)
|
81
|
+
end
|
82
|
+
|
83
|
+
def upload_product(product)
|
84
|
+
puts "Uploading product #{product.name}"
|
85
|
+
opsmanager_client.upload_product(product)
|
86
|
+
Tile.new(name: product.name, version: product.version)
|
87
|
+
end
|
88
|
+
|
89
|
+
def product_tiles
|
90
|
+
tiles.reject{|t| ['cf', 'microbosh', 'p-bosh'].include?(t.name) }
|
91
|
+
end
|
92
|
+
|
93
|
+
def tiles
|
94
|
+
opsmanager_client.send(:installed_products).map {|tempest_product|
|
95
|
+
Tile.new(
|
96
|
+
name: tempest_product.type,
|
97
|
+
version: tempest_product.version,
|
98
|
+
guid: tempest_product.guid
|
99
|
+
)
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
def vms_for_job_type(job_type)
|
104
|
+
opsmanager_client.vms_for_job_type(job_type)
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
attr_reader :username, :password, :page, :log_fetcher
|
110
|
+
|
111
|
+
def default_capybara_session
|
112
|
+
Capybara::Session.new(Capybara.default_driver)
|
113
|
+
end
|
114
|
+
|
115
|
+
def web_app_internals
|
116
|
+
@web_app_internals ||= WebAppInternals.new(dashboard, opsmanager_client, page, url)
|
117
|
+
end
|
118
|
+
|
119
|
+
def opsmanager_client
|
120
|
+
@opsmanager_client ||= ::OpsmanagerClient::Client.new(url, username, password)
|
121
|
+
end
|
122
|
+
|
123
|
+
def dashboard
|
124
|
+
@dashboard ||= login_page.login.tap { page.visit(url) unless page.current_url == url }
|
125
|
+
end
|
126
|
+
|
127
|
+
def login_page
|
128
|
+
@login_page ||= WebAppInternals::Page::Login.new(page: page, url: url, username: username, password: password)
|
129
|
+
end
|
130
|
+
|
131
|
+
def null_log_fetcher
|
132
|
+
NullLogFetcher.new
|
133
|
+
end
|
134
|
+
|
135
|
+
class NullLogFetcher
|
136
|
+
def fetch_logs(_, _)
|
137
|
+
return 'No Log fetcher configured.'
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,30 @@
|
|
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 OpsManagerLogFetcher
|
13
|
+
def initialize(ssh_gateway:, host:, username:, password:)
|
14
|
+
@ssh_gateway = ssh_gateway
|
15
|
+
@host = host
|
16
|
+
@username = username
|
17
|
+
@password = password
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch_logs(log_name, lines_from_tail = 0)
|
21
|
+
read_command = lines_from_tail > 0 ? "tail -n #{lines_from_tail}" : 'cat'
|
22
|
+
command = "#{read_command} /tmp/logs/#{log_name}"
|
23
|
+
ssh_gateway.execute_on(host, command, user: username, password: password)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :ssh_gateway, :host, :username, :password
|
29
|
+
end
|
30
|
+
end
|