opsmgr-cgfrost 0.35.9
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.txt +13 -0
- data/README.md +123 -0
- data/lib/opsmgr.rb +12 -0
- data/lib/opsmgr/api/client.rb +116 -0
- data/lib/opsmgr/api/http_client.rb +399 -0
- data/lib/opsmgr/api/results.rb +280 -0
- data/lib/opsmgr/api/version20/endpoints.rb +121 -0
- data/lib/opsmgr/bosh_command_runner.rb +84 -0
- data/lib/opsmgr/cmd/bosh_command.rb +189 -0
- data/lib/opsmgr/cmd/ops_manager.rb +142 -0
- data/lib/opsmgr/environments.rb +106 -0
- data/lib/opsmgr/errand_runner.rb +62 -0
- data/lib/opsmgr/log.rb +70 -0
- data/lib/opsmgr/product_upload_wrapper.rb +71 -0
- data/lib/opsmgr/renderer.rb +23 -0
- data/lib/opsmgr/renderer/aws.rb +148 -0
- data/lib/opsmgr/renderer/noop.rb +21 -0
- data/lib/opsmgr/settings/microbosh/installation_settings.rb +84 -0
- data/lib/opsmgr/settings/microbosh/job.rb +56 -0
- data/lib/opsmgr/settings/microbosh/job_list.rb +27 -0
- data/lib/opsmgr/settings/microbosh/network.rb +21 -0
- data/lib/opsmgr/settings/microbosh/product.rb +90 -0
- data/lib/opsmgr/settings/microbosh/product_list.rb +27 -0
- data/lib/opsmgr/settings/microbosh/property.rb +33 -0
- data/lib/opsmgr/settings/microbosh/property_list.rb +27 -0
- data/lib/opsmgr/tasks.rb +16 -0
- data/lib/opsmgr/tasks/bosh.rake +104 -0
- data/lib/opsmgr/tasks/destroy.rake +42 -0
- data/lib/opsmgr/tasks/info.rake +19 -0
- data/lib/opsmgr/tasks/opsmgr.rake +205 -0
- data/lib/opsmgr/tasks/product.rake +82 -0
- data/lib/opsmgr/ui_helpers/add_first_user_spec.rb +25 -0
- data/lib/opsmgr/ui_helpers/config_helper.rb +38 -0
- data/lib/opsmgr/ui_helpers/delete_installation_spec.rb +41 -0
- data/lib/opsmgr/ui_helpers/delete_product_if_present_spec.rb +37 -0
- data/lib/opsmgr/ui_helpers/delete_product_spec.rb +39 -0
- data/lib/opsmgr/ui_helpers/export_installation_spec.rb +31 -0
- data/lib/opsmgr/ui_helpers/get_latest_install_log_spec.rb +29 -0
- data/lib/opsmgr/ui_helpers/import_stemcell_spec.rb +30 -0
- data/lib/opsmgr/ui_helpers/microbosh/configure_microbosh_spec.rb +32 -0
- data/lib/opsmgr/ui_helpers/post_import_configuration_spec.rb +31 -0
- data/lib/opsmgr/ui_helpers/revert_staged_changes_spec.rb +39 -0
- data/lib/opsmgr/ui_helpers/settings_helper.rb +132 -0
- data/lib/opsmgr/ui_helpers/trigger_install_spec.rb +35 -0
- data/lib/opsmgr/ui_helpers/ui_spec_runner.rb +122 -0
- data/lib/opsmgr/ui_helpers/uncheck_errands_spec.rb +29 -0
- data/lib/opsmgr/ui_helpers/upload_and_add_product_spec.rb +27 -0
- data/lib/opsmgr/ui_helpers/upload_and_upgrade_product_spec.rb +36 -0
- data/lib/opsmgr/version.rb +11 -0
- data/sample_env_files/aws.yml +95 -0
- data/sample_env_files/vsphere.yml +87 -0
- metadata +392 -0
@@ -0,0 +1,280 @@
|
|
1
|
+
require 'opsmgr/settings/microbosh/installation_settings'
|
2
|
+
require 'opsmgr/settings/microbosh/product_list'
|
3
|
+
require 'opsmgr/settings/microbosh/job_list'
|
4
|
+
require 'opsmgr/settings/microbosh/property_list'
|
5
|
+
|
6
|
+
module Opsmgr
|
7
|
+
module Api
|
8
|
+
class Result
|
9
|
+
attr_reader :message
|
10
|
+
|
11
|
+
def success?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class InstallStatusResult < Result
|
17
|
+
attr_reader :status
|
18
|
+
|
19
|
+
def initialize(status_hash)
|
20
|
+
@status = status_hash['status']
|
21
|
+
status.freeze
|
22
|
+
|
23
|
+
fail(ArgumentError, "Unrecognized status: #{status}") unless %w(failed success running).include?(status)
|
24
|
+
end
|
25
|
+
|
26
|
+
def failed?
|
27
|
+
@status == 'failed'
|
28
|
+
end
|
29
|
+
|
30
|
+
def pending?
|
31
|
+
@status == 'running'
|
32
|
+
end
|
33
|
+
|
34
|
+
def finished_successfully?
|
35
|
+
@status == 'success'
|
36
|
+
end
|
37
|
+
|
38
|
+
def finished?
|
39
|
+
failed? || finished_successfully?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class InstallationDeletionStatusResult < Result
|
44
|
+
NO_INSTALLATION = 'There is no installation.'.freeze
|
45
|
+
DELETE_IN_PROGRESS = 'Delete in progress'.freeze
|
46
|
+
|
47
|
+
attr_reader :status
|
48
|
+
|
49
|
+
def initialize(status_hash)
|
50
|
+
@status = status_hash['status']
|
51
|
+
end
|
52
|
+
|
53
|
+
def in_progress?
|
54
|
+
status == DELETE_IN_PROGRESS
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class InstallLogResult < Result
|
59
|
+
attr_reader :log
|
60
|
+
|
61
|
+
def initialize(log_hash)
|
62
|
+
@log = log_hash['logs'].dup.freeze
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class TriggerInstallResult < Result
|
67
|
+
attr_reader :install_id
|
68
|
+
|
69
|
+
def initialize(install_hash)
|
70
|
+
@install_id = install_hash['install']['id']
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class ListProductsResult < Result
|
75
|
+
Product = Struct.new(:name, :product_version)
|
76
|
+
|
77
|
+
attr_reader :products
|
78
|
+
|
79
|
+
def initialize(product_hash_array)
|
80
|
+
@products = product_hash_array.map { |p| Product.new(p.fetch('name'), p.fetch('product_version')) }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class InstalledProductsResult < Result
|
85
|
+
def initialize(installed_products)
|
86
|
+
@installed_products = installed_products
|
87
|
+
end
|
88
|
+
|
89
|
+
def guid_for(product_type)
|
90
|
+
product_result = installed_products.find { |h| h.name == product_type }
|
91
|
+
product_result ? product_result.fetch('guid') : nil
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
attr_reader :installed_products
|
97
|
+
end
|
98
|
+
|
99
|
+
class InstallationSettingsResult < Result
|
100
|
+
def initialize(install_hash)
|
101
|
+
@install_hash = install_hash
|
102
|
+
end
|
103
|
+
|
104
|
+
def director_ip
|
105
|
+
ips_for_job(product_name: 'p-bosh', job_name: 'director').first ||
|
106
|
+
ips_for_job(product_name: 'microbosh', job_name: 'director').first
|
107
|
+
end
|
108
|
+
|
109
|
+
def ips_for_job(product_name:, job_name:)
|
110
|
+
product = products.find { |p| p.name == product_name }
|
111
|
+
return [] if product.nil?
|
112
|
+
|
113
|
+
if install_hash['ip_assignments']
|
114
|
+
install_hash['ip_assignments']['assignments'][product['guid']].each do |job_guid, assignment|
|
115
|
+
return assignment.values.flatten if job_guid.starts_with?(job_name)
|
116
|
+
end
|
117
|
+
else
|
118
|
+
product.fetch('ips').each do |job_guid, ips|
|
119
|
+
return ips if job_guid.starts_with?(job_name)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
[]
|
123
|
+
end
|
124
|
+
|
125
|
+
def director_password
|
126
|
+
microbosh_jobs = microbosh_settings.fetch('jobs')
|
127
|
+
microbosh_job_list = Opsmgr::Settings::Microbosh::JobList.new(microbosh_jobs)
|
128
|
+
director_job = microbosh_job_list.find { |job| job.name == 'director' }
|
129
|
+
properties = director_job.fetch('properties')
|
130
|
+
property_list = Opsmgr::Settings::Microbosh::PropertyList.new(properties)
|
131
|
+
credential_property = property_list.find { |property| property.name == 'director_credentials' }
|
132
|
+
credential_property.fetch('value').fetch('password')
|
133
|
+
end
|
134
|
+
|
135
|
+
def uaa_admin_password
|
136
|
+
jobs = cf_settings.fetch('jobs')
|
137
|
+
job_list = Opsmgr::Settings::Microbosh::JobList.new(jobs)
|
138
|
+
uaa_job = job_list.find { |job| job.name == 'uaa' }
|
139
|
+
properties = uaa_job.fetch('properties')
|
140
|
+
property_list = Opsmgr::Settings::Microbosh::PropertyList.new(properties)
|
141
|
+
credential_property = property_list.find { |property| property.name == 'admin_credentials' }
|
142
|
+
credential_property.fetch('value').fetch('password')
|
143
|
+
end
|
144
|
+
|
145
|
+
def system_domain
|
146
|
+
properties = cc_job.fetch('properties')
|
147
|
+
property_list = Opsmgr::Settings::Microbosh::PropertyList.new(properties)
|
148
|
+
credential_property = property_list.find { |property| property.name == 'system_domain' }
|
149
|
+
credential_property.fetch('value')
|
150
|
+
end
|
151
|
+
|
152
|
+
def apps_domain
|
153
|
+
properties = cc_job.fetch('properties')
|
154
|
+
property_list = Opsmgr::Settings::Microbosh::PropertyList.new(properties)
|
155
|
+
credential_property = property_list.find { |property| property.name == 'apps_domain' }
|
156
|
+
credential_property.fetch('value')
|
157
|
+
end
|
158
|
+
|
159
|
+
def product_version(product_guid)
|
160
|
+
product = products.find { |p| p.guid == product_guid }
|
161
|
+
return nil if product.nil?
|
162
|
+
product.fetch('product_version')
|
163
|
+
end
|
164
|
+
|
165
|
+
def product_version_by_name(product_name)
|
166
|
+
product = products.find { |p| p.name == product_name }
|
167
|
+
return nil if product.nil?
|
168
|
+
product.fetch('product_version')
|
169
|
+
end
|
170
|
+
|
171
|
+
def ops_manager_installation_settings
|
172
|
+
Opsmgr::Settings::Microbosh::InstallationSettings.from_api_result(self)
|
173
|
+
end
|
174
|
+
|
175
|
+
def as_hash
|
176
|
+
install_hash
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
attr_reader :install_hash
|
182
|
+
|
183
|
+
def microbosh_settings
|
184
|
+
products.find { |product| product.name == 'p-bosh' || product.name == 'microbosh' } || fail('microbosh not found')
|
185
|
+
end
|
186
|
+
|
187
|
+
def cf_settings
|
188
|
+
products.find { |product| product.name == 'cf' } || fail('cf not found')
|
189
|
+
end
|
190
|
+
|
191
|
+
def products
|
192
|
+
if install_hash.key?('products')
|
193
|
+
Opsmgr::Settings::Microbosh::ProductList.new(install_hash.fetch('products'))
|
194
|
+
elsif install_hash.key?('components')
|
195
|
+
Opsmgr::Settings::Microbosh::ProductList.new(install_hash.fetch('components'))
|
196
|
+
else
|
197
|
+
fail "Unable to find products in OpsManager response. Check installation schema: #{install_hash.inspect}."
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def cc_job
|
202
|
+
cf_settings.fetch('jobs')
|
203
|
+
cf_jobs = cf_settings.fetch('jobs')
|
204
|
+
cf_job_list = Opsmgr::Settings::Microbosh::JobList.new(cf_jobs)
|
205
|
+
cf_job_list.find { |job| job.name == 'cloud_controller' }
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
class ProductManifestResult < Result
|
210
|
+
Product = Struct.new(:name, :version)
|
211
|
+
Stemcell = Struct.new(:name, :version)
|
212
|
+
|
213
|
+
def initialize(manifest_hash)
|
214
|
+
@manifest_hash = manifest_hash
|
215
|
+
end
|
216
|
+
|
217
|
+
def releases
|
218
|
+
manifest_hash.fetch('releases').map { |h| Product.new(h.fetch('name'), h.fetch('version')) }
|
219
|
+
end
|
220
|
+
|
221
|
+
def stemcell
|
222
|
+
stemcell_hash = manifest_hash.fetch('resource_pools').first.fetch('stemcell')
|
223
|
+
Stemcell.new(stemcell_hash.fetch('name'), stemcell_hash.fetch('version'))
|
224
|
+
end
|
225
|
+
|
226
|
+
private
|
227
|
+
|
228
|
+
attr_reader :manifest_hash
|
229
|
+
end
|
230
|
+
|
231
|
+
class CreateFirstUserResult < Result
|
232
|
+
attr_reader :user_id
|
233
|
+
|
234
|
+
def initialize(user_hash)
|
235
|
+
@user_id = user_hash['user']['id']
|
236
|
+
end
|
237
|
+
|
238
|
+
def user_already_existed?
|
239
|
+
false
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
class StagedManifestResult < Result
|
244
|
+
attr_reader :manifest
|
245
|
+
|
246
|
+
def initialize(manifest)
|
247
|
+
@manifest = manifest
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
class Error < Result
|
252
|
+
def initialize(message, response)
|
253
|
+
@message = "#{message}:\n#{response.body}"
|
254
|
+
@response = response
|
255
|
+
end
|
256
|
+
|
257
|
+
def success?
|
258
|
+
false
|
259
|
+
end
|
260
|
+
|
261
|
+
private
|
262
|
+
|
263
|
+
attr_reader :response
|
264
|
+
end
|
265
|
+
|
266
|
+
class CreateFirstUserError < Error
|
267
|
+
def user_already_existed?
|
268
|
+
response.code == '401'
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
274
|
+
# All rights reserved.
|
275
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
276
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
277
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
278
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
279
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
280
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Opsmgr
|
2
|
+
module Api
|
3
|
+
module Version20
|
4
|
+
class Endpoints
|
5
|
+
VERSION = '2.0'.freeze
|
6
|
+
|
7
|
+
def version
|
8
|
+
VERSION
|
9
|
+
end
|
10
|
+
|
11
|
+
def installation_settings_post_path
|
12
|
+
'/api/installation_settings'
|
13
|
+
end
|
14
|
+
|
15
|
+
def installation_settings_get_path
|
16
|
+
'/api/installation_settings'
|
17
|
+
end
|
18
|
+
|
19
|
+
def install_post_path
|
20
|
+
'/api/installation?ignore_warnings=1'
|
21
|
+
end
|
22
|
+
|
23
|
+
def installation_path
|
24
|
+
'/api/installation'
|
25
|
+
end
|
26
|
+
|
27
|
+
def api_user_path
|
28
|
+
'/api/user'
|
29
|
+
end
|
30
|
+
|
31
|
+
def api_setup_path
|
32
|
+
'/api/setup'
|
33
|
+
end
|
34
|
+
|
35
|
+
def show_installation_status(id)
|
36
|
+
"/api/installation/#{id}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def installation_log_path(id)
|
40
|
+
"/api/installation/#{id}/logs"
|
41
|
+
end
|
42
|
+
|
43
|
+
def upload_product_path
|
44
|
+
'/api/products'
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_product_path
|
48
|
+
'/api/installation_settings/products'
|
49
|
+
end
|
50
|
+
|
51
|
+
def upgrade_product_path(product_guid)
|
52
|
+
"/api/installation_settings/products/#{product_guid}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def upload_product_form_key
|
56
|
+
'product'
|
57
|
+
end
|
58
|
+
|
59
|
+
def list_components_path
|
60
|
+
'/api/components'
|
61
|
+
end
|
62
|
+
|
63
|
+
def list_products_path
|
64
|
+
'/api/products'
|
65
|
+
end
|
66
|
+
|
67
|
+
def installed_products_path
|
68
|
+
'/api/installation_settings/products'
|
69
|
+
end
|
70
|
+
|
71
|
+
def product_manifest_path(product_guid)
|
72
|
+
"/api/manifests/#{product_guid}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def download_staged_manifest(product_guid)
|
76
|
+
"/api/v0/staged/products/#{product_guid}/manifest"
|
77
|
+
end
|
78
|
+
|
79
|
+
def export_installation_path
|
80
|
+
'/api/installation_asset_collection'
|
81
|
+
end
|
82
|
+
|
83
|
+
def import_installation_path
|
84
|
+
'/api/installation_asset_collection'
|
85
|
+
end
|
86
|
+
|
87
|
+
def mark_for_deletion_path(product_guid)
|
88
|
+
"/api/installation_settings/products/#{product_guid}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def import_stemcell_path
|
92
|
+
'/api/stemcells'
|
93
|
+
end
|
94
|
+
|
95
|
+
def root_ca_certificate_path
|
96
|
+
'/api/v0/security/root_ca_certificate'
|
97
|
+
end
|
98
|
+
|
99
|
+
def uaa_get_user_by_username_path(username)
|
100
|
+
"/uaa/Users?attributes=id&filter=userName%20eq%20'#{username}'"
|
101
|
+
end
|
102
|
+
|
103
|
+
def uaa_create_user
|
104
|
+
'/uaa/Users'
|
105
|
+
end
|
106
|
+
|
107
|
+
def login_ensure_availability
|
108
|
+
'/login/ensure_availability'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
115
|
+
# All rights reserved.
|
116
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
117
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
118
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
119
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
120
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
121
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Opsmgr
|
2
|
+
class BoshCommandRunner
|
3
|
+
def initialize(bosh_command:, logger:)
|
4
|
+
@bosh_command = bosh_command
|
5
|
+
@logger = logger
|
6
|
+
end
|
7
|
+
|
8
|
+
def run(command)
|
9
|
+
system_or_fail(
|
10
|
+
"#{bosh_command_prefix} #{command}",
|
11
|
+
"bosh #{command} failed"
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def run_and_capture_output(command)
|
16
|
+
capture_output_or_fail(
|
17
|
+
"#{bosh_command_prefix} #{command}",
|
18
|
+
"bosh #{command} failed"
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def run_with_deployment(command, deployment)
|
23
|
+
bosh_deployment = bosh_deployments[/#{deployment}-[0-9a-f]{8,}/]
|
24
|
+
fail 'Deployment not found' unless bosh_deployment
|
25
|
+
|
26
|
+
deployment_file = "#{ENV.fetch('TMPDIR', '/tmp')}/#{deployment}.yml"
|
27
|
+
|
28
|
+
begin
|
29
|
+
run(
|
30
|
+
"-n download manifest #{bosh_deployment} #{deployment_file}"
|
31
|
+
)
|
32
|
+
rescue RuntimeError
|
33
|
+
raise 'bosh download manifest failed'
|
34
|
+
end
|
35
|
+
|
36
|
+
run("-d #{deployment_file} #{command}")
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def bosh_deployments
|
42
|
+
run_and_capture_output('deployments')
|
43
|
+
rescue RuntimeError
|
44
|
+
raise 'bosh deployments failed'
|
45
|
+
end
|
46
|
+
|
47
|
+
def system_or_fail(command, failure_message)
|
48
|
+
clean_and_log(command)
|
49
|
+
Bundler.clean_system(command) || fail(failure_message)
|
50
|
+
end
|
51
|
+
|
52
|
+
def capture_output_or_fail(command, failure_message)
|
53
|
+
clean_and_log(command)
|
54
|
+
|
55
|
+
Bundler.with_clean_env do
|
56
|
+
output, status = Open3.capture2(command)
|
57
|
+
fail(failure_message) unless status.success?
|
58
|
+
output
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def clean_and_log(command)
|
63
|
+
scrubbed_command = command.slice(command.index('bosh')..-1)
|
64
|
+
scrubbed_command.sub!(/\s\-p\s\b\w+\b/, ' -p <PASSWORD>')
|
65
|
+
scrubbed_command.sub!(/\s\-u\s\b\w+\b/, ' -u <USER>')
|
66
|
+
|
67
|
+
logger.info("Running #{scrubbed_command}")
|
68
|
+
end
|
69
|
+
|
70
|
+
def bosh_command_prefix
|
71
|
+
@bosh_command_prefix ||= bosh_command.command
|
72
|
+
end
|
73
|
+
|
74
|
+
attr_reader :bosh_command, :logger
|
75
|
+
end
|
76
|
+
end
|
77
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
78
|
+
# All rights reserved.
|
79
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
80
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
81
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
82
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
83
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
84
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|