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,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
|