bosh-cloudfoundry 0.2.0
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.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/Gemfile +6 -0
- data/Guardfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +242 -0
- data/Rakefile +33 -0
- data/bosh-cloudfoundry.gemspec +29 -0
- data/config/defaults.yml +6 -0
- data/lib/bosh-cloudfoundry.rb +34 -0
- data/lib/bosh-cloudfoundry/bosh_release_manager.rb +141 -0
- data/lib/bosh-cloudfoundry/config.rb +6 -0
- data/lib/bosh-cloudfoundry/config/common_config.rb +27 -0
- data/lib/bosh-cloudfoundry/config/dea_config.rb +150 -0
- data/lib/bosh-cloudfoundry/config/microbosh_config.rb +106 -0
- data/lib/bosh-cloudfoundry/config/postgresql_service_config.rb +185 -0
- data/lib/bosh-cloudfoundry/config/redis_service_config.rb +179 -0
- data/lib/bosh-cloudfoundry/config/system_config.rb +60 -0
- data/lib/bosh-cloudfoundry/config_options.rb +362 -0
- data/lib/bosh-cloudfoundry/gerrit_patches_helper.rb +47 -0
- data/lib/bosh-cloudfoundry/providers.rb +19 -0
- data/lib/bosh-cloudfoundry/providers/aws.rb +75 -0
- data/lib/bosh-cloudfoundry/system_deployment_manifest_renderer.rb +286 -0
- data/lib/bosh-cloudfoundry/version.rb +5 -0
- data/lib/bosh/cli/commands/cf.rb +668 -0
- data/spec/assets/.gitkeep +0 -0
- data/spec/assets/cf-release/jobs/cloud_controller/templates/runtimes.yml +150 -0
- data/spec/assets/deployments/aws-core-1-m1.small-free-redis.yml +221 -0
- data/spec/assets/deployments/aws-core-1-m1.xlarge-free-postgresql-2-m1.small-free-postgresql.yml +248 -0
- data/spec/assets/deployments/aws-core-2-m1.xlarge-dea.yml +195 -0
- data/spec/assets/deployments/aws-core-only.yml +178 -0
- data/spec/assets/deployments/aws-core-with-nfs.yml +192 -0
- data/spec/functional/.gitkeep +0 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/unit/.gitkeep +0 -0
- data/spec/unit/bosh_release_manager_spec.rb +36 -0
- data/spec/unit/cf_command_spec.rb +280 -0
- data/spec/unit/config/common_config_spec.rb +21 -0
- data/spec/unit/config/dea_config_spec.rb +92 -0
- data/spec/unit/config/microbosh_config_spec.rb +11 -0
- data/spec/unit/config/postgresql_service_config_spec.rb +103 -0
- data/spec/unit/config/redis_service_config_spec.rb +103 -0
- data/spec/unit/config/system_config_spec.rb +29 -0
- data/spec/unit/config_options_spec.rb +64 -0
- data/spec/unit/system_deployment_manifest_renderer_spec.rb +93 -0
- metadata +246 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
module Bosh; module CloudFoundry; end; end
|
4
|
+
|
5
|
+
# There are two concepts of "latest".
|
6
|
+
# * for upload: "latest" is the highest release in cf-release
|
7
|
+
# * for manifest creation: "latest" is the highest release already uploaded to the BOSH
|
8
|
+
module Bosh::CloudFoundry::GerritPatchesHelper
|
9
|
+
|
10
|
+
def extract_refs_change(gerrit_change)
|
11
|
+
if gerrit_change =~ %r{(\d+)/(\d+)/(\d+)$}
|
12
|
+
"#{$1}/#{$2}/#{$3}"
|
13
|
+
else
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_gerrit_refs_change(refs_change)
|
19
|
+
system_config.gerrit_changes ||= []
|
20
|
+
unless system_config.gerrit_changes.include?(refs_change)
|
21
|
+
system_config.gerrit_changes << refs_change
|
22
|
+
system_config.save
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def apply_gerrit_patches
|
27
|
+
# is the gerrit setup necessary; or can use anonymous HTTP?
|
28
|
+
# confirm_gerrit_username # http://reviews.cloudfoundry.org/#/settings/
|
29
|
+
# confirm_user_added_vcap_ssh_keys_to_gerrit # http://reviews.cloudfoundry.org/#/settings/ssh-keys
|
30
|
+
# confirm_ssh_access # ssh -p 29418 drnic@reviews.cloudfoundry.org 2>&1 | grep "Permission denied"
|
31
|
+
create_and_change_into_patches_branch
|
32
|
+
ssh_uri = "http://reviews.cloudfoundry.org/cf-release"
|
33
|
+
chdir(cf_release_dir) do
|
34
|
+
system_config.gerrit_changes.each do |refs_change|
|
35
|
+
sh "git pull #{ssh_uri} refs/changes/#{refs_change}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_and_change_into_patches_branch
|
41
|
+
chdir(cf_release_dir) do
|
42
|
+
sh "git checkout master"
|
43
|
+
sh "git branch -f patches" # force create
|
44
|
+
sh "git checkout patches"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
module Bosh; module CloudFoundry; end; end
|
4
|
+
|
5
|
+
module Bosh::CloudFoundry::Providers
|
6
|
+
extend self
|
7
|
+
# returns a BOSH provider (CPI) specific object
|
8
|
+
# with helpers related to that provider
|
9
|
+
def for_bosh_provider_name(system_config)
|
10
|
+
case system_config.bosh_provider.to_sym
|
11
|
+
when :aws
|
12
|
+
Bosh::CloudFoundry::Providers::AWS.new(system_config.microbosh.fog_compute)
|
13
|
+
else
|
14
|
+
raise "please support #{system_config.bosh_provider} provider"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
require "bosh-cloudfoundry/providers/aws"
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
module Bosh; module CloudFoundry; module Providers; end; end; end
|
4
|
+
|
5
|
+
class Bosh::CloudFoundry::Providers::AWS
|
6
|
+
attr_reader :fog_compute
|
7
|
+
def initialize(fog_compute=nil)
|
8
|
+
@fog_compute = fog_compute
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [Integer] megabytes of RAM for requested flavor of server
|
12
|
+
def ram_for_server_flavor(server_flavor_id)
|
13
|
+
if flavor = fog_compute_flavor(server_flavor_id)
|
14
|
+
flavor[:ram]
|
15
|
+
else
|
16
|
+
raise "Unknown AWS flavor '#{server_flavor_id}'"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Hash] e.g. { :bits => 0, :cores => 2, :disk => 0,
|
21
|
+
# :id => 't1.micro', :name => 'Micro Instance', :ram => 613}
|
22
|
+
# or nil if +server_flavor_id+ is not a supported flavor ID
|
23
|
+
def fog_compute_flavor(server_flavor_id)
|
24
|
+
aws_compute_flavors.find { |fl| fl[:id] == server_flavor_id }
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Array] of [Hash] for each supported compute flavor
|
28
|
+
# Example [Hash] { :bits => 0, :cores => 2, :disk => 0,
|
29
|
+
# :id => 't1.micro', :name => 'Micro Instance', :ram => 613}
|
30
|
+
def aws_compute_flavors
|
31
|
+
Fog::Compute::AWS::FLAVORS
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [String] provisions a new public IP address in target region
|
35
|
+
# TODO nil if none available
|
36
|
+
def provision_public_ip_address
|
37
|
+
return unless fog_compute
|
38
|
+
address = fog_compute.addresses.create
|
39
|
+
address.public_ip
|
40
|
+
# TODO catch error and return nil
|
41
|
+
end
|
42
|
+
|
43
|
+
# Creates or reuses an AWS security group and opens ports.
|
44
|
+
#
|
45
|
+
# +security_group_name+ is the name to be created or reused
|
46
|
+
# +ports+ is a hash of name/port for ports to open, for example:
|
47
|
+
# {
|
48
|
+
# ssh: 22,
|
49
|
+
# http: 80,
|
50
|
+
# https: 443
|
51
|
+
# }
|
52
|
+
def create_security_group(security_group_name, ports)
|
53
|
+
unless sg = fog_compute.security_groups.get(security_group_name)
|
54
|
+
sg = fog_compute.security_groups.create(name: security_group_name, description: "microbosh")
|
55
|
+
puts "Created security group #{security_group_name}"
|
56
|
+
else
|
57
|
+
puts "Reusing security group #{security_group_name}"
|
58
|
+
end
|
59
|
+
ip_permissions = sg.ip_permissions
|
60
|
+
ports_opened = 0
|
61
|
+
ports.each do |name, port|
|
62
|
+
unless port_open?(ip_permissions, port)
|
63
|
+
sg.authorize_port_range(port..port)
|
64
|
+
puts " -> opened #{name} port #{port}"
|
65
|
+
ports_opened += 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
puts " -> no additional ports opened" if ports_opened == 0
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
def port_open?(ip_permissions, port)
|
73
|
+
ip_permissions && ip_permissions.find {|ip| ip["fromPort"] <= port && ip["toPort"] >= port }
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,286 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
module Bosh; module CloudFoundry; end; end
|
4
|
+
|
5
|
+
# Renders a +SystemConfig+ model into a System's BOSH deployment
|
6
|
+
# manifest(s).
|
7
|
+
class Bosh::CloudFoundry::SystemDeploymentManifestRenderer
|
8
|
+
include FileUtils
|
9
|
+
attr_reader :system_config, :common_config, :bosh_config
|
10
|
+
|
11
|
+
def initialize(system_config, common_config, bosh_config)
|
12
|
+
@system_config = system_config
|
13
|
+
@common_config = common_config
|
14
|
+
@bosh_config = bosh_config
|
15
|
+
end
|
16
|
+
|
17
|
+
# Render deployment manifest(s) for a system
|
18
|
+
# based on the model data in +system_config+
|
19
|
+
# (a +SystemConfig+ object).
|
20
|
+
def perform
|
21
|
+
validate_system_config
|
22
|
+
|
23
|
+
deployment_name = "#{system_config.system_name}-core"
|
24
|
+
|
25
|
+
manifest = base_manifest(
|
26
|
+
deployment_name,
|
27
|
+
bosh_config.target_uuid,
|
28
|
+
system_config.bosh_provider,
|
29
|
+
system_config.system_name,
|
30
|
+
system_config.release_name,
|
31
|
+
system_config.release_version,
|
32
|
+
system_config.stemcell_name,
|
33
|
+
system_config.stemcell_version,
|
34
|
+
cloud_properties_for_server_flavor(system_config.core_server_flavor),
|
35
|
+
system_config.core_ip,
|
36
|
+
system_config.root_dns,
|
37
|
+
system_config.admin_emails,
|
38
|
+
system_config.common_password,
|
39
|
+
system_config.common_persistent_disk,
|
40
|
+
system_config.security_group
|
41
|
+
)
|
42
|
+
|
43
|
+
dea_config.add_core_jobs_to_manifest(manifest)
|
44
|
+
dea_config.add_resource_pools_to_manifest(manifest)
|
45
|
+
dea_config.add_jobs_to_manifest(manifest)
|
46
|
+
dea_config.merge_manifest_properties(manifest)
|
47
|
+
|
48
|
+
postgresql_service_config.add_core_jobs_to_manifest(manifest)
|
49
|
+
postgresql_service_config.add_resource_pools_to_manifest(manifest)
|
50
|
+
postgresql_service_config.add_jobs_to_manifest(manifest)
|
51
|
+
postgresql_service_config.merge_manifest_properties(manifest)
|
52
|
+
|
53
|
+
redis_service_config.add_core_jobs_to_manifest(manifest)
|
54
|
+
redis_service_config.add_resource_pools_to_manifest(manifest)
|
55
|
+
redis_service_config.add_jobs_to_manifest(manifest)
|
56
|
+
redis_service_config.merge_manifest_properties(manifest)
|
57
|
+
|
58
|
+
chdir system_config.system_dir do
|
59
|
+
mkdir_p("deployments")
|
60
|
+
File.open("deployments/#{system_config.system_name}-core.yml", "w") do |file|
|
61
|
+
file << manifest.to_yaml
|
62
|
+
end
|
63
|
+
# `open "deployments/#{system_config.system_name}-core.yml"`
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def validate_system_config
|
68
|
+
s = system_config
|
69
|
+
must_not_be_nil = [
|
70
|
+
:system_dir,
|
71
|
+
:bosh_provider,
|
72
|
+
:release_name,
|
73
|
+
:release_version,
|
74
|
+
:stemcell_name,
|
75
|
+
:stemcell_version,
|
76
|
+
:core_server_flavor,
|
77
|
+
:core_ip,
|
78
|
+
:root_dns,
|
79
|
+
:admin_emails,
|
80
|
+
:common_password,
|
81
|
+
:common_persistent_disk,
|
82
|
+
:security_group,
|
83
|
+
]
|
84
|
+
must_not_be_nil_failures = must_not_be_nil.inject([]) do |list, attribute|
|
85
|
+
list << attribute unless system_config.send(attribute)
|
86
|
+
list
|
87
|
+
end
|
88
|
+
if must_not_be_nil_failures.size > 0
|
89
|
+
raise "These SystemConfig fields must not be nil: #{must_not_be_nil_failures.inspect}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def dea_config
|
94
|
+
@dea_config ||= Bosh::CloudFoundry::Config::DeaConfig.build_from_system_config(system_config)
|
95
|
+
end
|
96
|
+
|
97
|
+
def postgresql_service_config
|
98
|
+
@postgresql_service_config ||=
|
99
|
+
Bosh::CloudFoundry::Config::PostgresqlServiceConfig.build_from_system_config(system_config)
|
100
|
+
end
|
101
|
+
|
102
|
+
def redis_service_config
|
103
|
+
@redis_service_config ||=
|
104
|
+
Bosh::CloudFoundry::Config::RedisServiceConfig.build_from_system_config(system_config)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Converts a server flavor (such as 'm1.large' on AWS) into
|
108
|
+
# a BOSH deployment manifest +cloud_properties+ YAML string
|
109
|
+
# For AWS & m1.large, it would be:
|
110
|
+
# 'instance_type: m1.large'
|
111
|
+
def cloud_properties_for_server_flavor(server_flavor)
|
112
|
+
if aws?
|
113
|
+
{ "instance_type" => server_flavor }
|
114
|
+
else
|
115
|
+
raise 'Please implement #{self.class}#cloud_properties_for_server_flavor'
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def aws?
|
120
|
+
system_config.bosh_provider == "aws"
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
def base_manifest(
|
125
|
+
deployment_name,
|
126
|
+
director_uuid,
|
127
|
+
bosh_provider,
|
128
|
+
system_name,
|
129
|
+
release_name,
|
130
|
+
release_version,
|
131
|
+
stemcell_name,
|
132
|
+
stemcell_version,
|
133
|
+
core_cloud_properties,
|
134
|
+
core_ip,
|
135
|
+
root_dns,
|
136
|
+
admin_emails,
|
137
|
+
common_password,
|
138
|
+
common_persistent_disk,
|
139
|
+
security_group
|
140
|
+
)
|
141
|
+
# This large, terse, pretty-printed manifest can be
|
142
|
+
# generated by loading in a spec/assets/deployments/*.yml file
|
143
|
+
# and pretty-printing it.
|
144
|
+
#
|
145
|
+
# manifest = YAML.load_file('spec/assets/deployments/aws-core-only.yml')
|
146
|
+
# require "pp"
|
147
|
+
# pp manifest
|
148
|
+
{"name"=>deployment_name,
|
149
|
+
"director_uuid"=>director_uuid,
|
150
|
+
"release"=>{"name"=>release_name, "version"=>release_version},
|
151
|
+
"compilation"=>
|
152
|
+
{"workers"=>10,
|
153
|
+
"network"=>"default",
|
154
|
+
"reuse_compilation_vms"=>true,
|
155
|
+
"cloud_properties"=>{"instance_type"=>"m1.medium"}},
|
156
|
+
"update"=>
|
157
|
+
{"canaries"=>1,
|
158
|
+
"canary_watch_time"=>"30000-150000",
|
159
|
+
"update_watch_time"=>"30000-150000",
|
160
|
+
"max_in_flight"=>4,
|
161
|
+
"max_errors"=>1},
|
162
|
+
"networks"=>
|
163
|
+
[{"name"=>"default",
|
164
|
+
"type"=>"dynamic",
|
165
|
+
"cloud_properties"=>{"security_groups"=>[security_group]}},
|
166
|
+
{"name"=>"vip_network",
|
167
|
+
"type"=>"vip",
|
168
|
+
"cloud_properties"=>{"security_groups"=>[security_group]}}],
|
169
|
+
"resource_pools"=>
|
170
|
+
[{"name"=>"core",
|
171
|
+
"network"=>"default",
|
172
|
+
"size"=>1,
|
173
|
+
"stemcell"=>{"name"=>stemcell_name, "version"=>stemcell_version},
|
174
|
+
"cloud_properties"=>core_cloud_properties,
|
175
|
+
"persistent_disk"=>common_persistent_disk}],
|
176
|
+
"jobs"=>
|
177
|
+
[{"name"=>"core",
|
178
|
+
"template"=>
|
179
|
+
["postgres",
|
180
|
+
"nats",
|
181
|
+
"router",
|
182
|
+
"health_manager",
|
183
|
+
"cloud_controller",
|
184
|
+
# "debian_nfs_server",
|
185
|
+
# "serialization_data_server",
|
186
|
+
"stager",
|
187
|
+
"uaa",
|
188
|
+
"vcap_redis"],
|
189
|
+
"instances"=>1,
|
190
|
+
"resource_pool"=>"core",
|
191
|
+
"networks"=>
|
192
|
+
[{"name"=>"default", "default"=>["dns", "gateway"]},
|
193
|
+
{"name"=>"vip_network", "static_ips"=>[core_ip]}],
|
194
|
+
"persistent_disk"=>common_persistent_disk}],
|
195
|
+
"properties"=>
|
196
|
+
{"domain"=>root_dns,
|
197
|
+
"env"=>nil,
|
198
|
+
"networks"=>{"apps"=>"default", "management"=>"default"},
|
199
|
+
"router"=>
|
200
|
+
{"client_inactivity_timeout"=>600,
|
201
|
+
"app_inactivity_timeout"=>600,
|
202
|
+
"local_route"=>core_ip,
|
203
|
+
"status"=>
|
204
|
+
{"port"=>8080, "user"=>"router", "password"=>common_password}},
|
205
|
+
"nats"=>
|
206
|
+
{"user"=>"nats",
|
207
|
+
"password"=>common_password,
|
208
|
+
"address"=>core_ip,
|
209
|
+
"port"=>4222},
|
210
|
+
"db"=>"ccdb",
|
211
|
+
"ccdb"=>
|
212
|
+
{"template"=>"postgres",
|
213
|
+
"address"=>core_ip,
|
214
|
+
"port"=>2544,
|
215
|
+
"databases"=>
|
216
|
+
[{"tag"=>"cc", "name"=>"appcloud"},
|
217
|
+
{"tag"=>"uaa", "name"=>"uaa"}],
|
218
|
+
"roles"=>
|
219
|
+
[{"name"=>"root", "password"=>common_password, "tag"=>"admin"},
|
220
|
+
{"name"=>"uaa", "password"=>common_password, "tag"=>"uaa"}]},
|
221
|
+
"cc"=>
|
222
|
+
{"description"=>"Cloud Foundry",
|
223
|
+
"srv_api_uri"=>"http://api.#{root_dns}",
|
224
|
+
"password"=>common_password,
|
225
|
+
"token"=>"TOKEN",
|
226
|
+
"allow_debug"=>true,
|
227
|
+
"allow_registration"=>true,
|
228
|
+
"admins"=>admin_emails,
|
229
|
+
"admin_account_capacity"=>
|
230
|
+
{"memory"=>2048, "app_uris"=>32, "services"=>16, "apps"=>16},
|
231
|
+
"default_account_capacity"=>
|
232
|
+
{"memory"=>2048, "app_uris"=>32, "services"=>16, "apps"=>16},
|
233
|
+
"new_stager_percent"=>100,
|
234
|
+
"staging_upload_user"=>"vcap",
|
235
|
+
"staging_upload_password"=>common_password,
|
236
|
+
"uaa"=>
|
237
|
+
{"enabled"=>true,
|
238
|
+
"resource_id"=>"cloud_controller",
|
239
|
+
"token_creation_email_filter"=>[""]},
|
240
|
+
"service_extension"=>{"service_lifecycle"=>{"max_upload_size"=>5}},
|
241
|
+
"use_nginx"=>false},
|
242
|
+
"postgresql_server"=>{"max_connections"=>30, "listen_address"=>"0.0.0.0"},
|
243
|
+
# "serialization_data_server"=>
|
244
|
+
# {"upload_token"=>"TOKEN",
|
245
|
+
# "use_nginx"=>false,
|
246
|
+
# "upload_timeout"=>10,
|
247
|
+
# "port"=>8090,
|
248
|
+
# "upload_file_expire_time"=>600,
|
249
|
+
# "purge_expired_interval"=>30},
|
250
|
+
"service_lifecycle"=>
|
251
|
+
{"download_url"=>core_ip,
|
252
|
+
"mount_point"=>"/var/vcap/service_lifecycle",
|
253
|
+
"tmp_dir"=>"/var/vcap/service_lifecycle/tmp_dir",
|
254
|
+
"resque"=>
|
255
|
+
{"host"=>core_ip, "port"=>3456, "password"=>common_password},
|
256
|
+
# "nfs_server"=>{"address"=>core_ip, "export_dir"=>"/cfsnapshot"},
|
257
|
+
# "serialization_data_server"=>[core_ip]
|
258
|
+
},
|
259
|
+
"stager"=>
|
260
|
+
{"max_staging_duration"=>120,
|
261
|
+
"max_active_tasks"=>20,
|
262
|
+
"queues"=>["staging"]},
|
263
|
+
"uaa"=>
|
264
|
+
{"cc"=>{"token_secret"=>"TOKEN_SECRET", "client_secret"=>"CLIENT_SECRET"},
|
265
|
+
"admin"=>{"client_secret"=>"CLIENT_SECRET"},
|
266
|
+
"login"=>{"client_secret"=>"CLIENT_SECRET"},
|
267
|
+
"batch"=>{"username"=>"uaa", "password"=>common_password},
|
268
|
+
"port"=>8100,
|
269
|
+
"catalina_opts"=>"-Xmx128m -Xms30m -XX:MaxPermSize=128m",
|
270
|
+
"no_ssl"=>true},
|
271
|
+
"uaadb"=>
|
272
|
+
{"address"=>core_ip,
|
273
|
+
"port"=>2544,
|
274
|
+
"roles"=>
|
275
|
+
[{"tag"=>"admin", "name"=>"uaa", "password"=>common_password}],
|
276
|
+
"databases"=>[{"tag"=>"uaa", "name"=>"uaa"}]},
|
277
|
+
"vcap_redis"=>
|
278
|
+
{"address"=>core_ip,
|
279
|
+
"port"=>3456,
|
280
|
+
"password"=>common_password,
|
281
|
+
"maxmemory"=>500000000},
|
282
|
+
"service_plans"=>{},
|
283
|
+
"dea"=>{"max_memory"=>512}}}
|
284
|
+
end
|
285
|
+
|
286
|
+
end
|
@@ -0,0 +1,668 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
require 'bosh-cloudfoundry'
|
4
|
+
|
5
|
+
module Bosh::Cli::Command
|
6
|
+
class CloudFoundry < Base
|
7
|
+
include Bosh::Cli::DeploymentHelper
|
8
|
+
include Bosh::Cli::VersionCalc
|
9
|
+
include Bosh::CloudFoundry::ConfigOptions
|
10
|
+
include Bosh::CloudFoundry::BoshReleaseManager
|
11
|
+
include Bosh::CloudFoundry::GerritPatchesHelper
|
12
|
+
include FileUtils
|
13
|
+
|
14
|
+
usage "cf"
|
15
|
+
desc "show cf bosh sub-commands"
|
16
|
+
def cf_help
|
17
|
+
say("bosh cf sub-commands:")
|
18
|
+
nl
|
19
|
+
cmds = Bosh::Cli::Config.commands.values.find_all {|c|
|
20
|
+
c.usage =~ /^cf/
|
21
|
+
}
|
22
|
+
Bosh::Cli::Command::Help.list_commands(cmds)
|
23
|
+
end
|
24
|
+
|
25
|
+
usage "cf prepare system"
|
26
|
+
desc "create CloudFoundry system"
|
27
|
+
option "--core-ip ip", String, "Static IP for CloudController/router, e.g. 1.2.3.4"
|
28
|
+
option "--root-dns dns", String, "Base DNS for CloudFoundry applications, e.g. vcap.me"
|
29
|
+
option "--core-server-flavor flavor", String,
|
30
|
+
"Flavor of the CloudFoundry Core server. Default: 'm1.large'"
|
31
|
+
option "--release-name name", String,
|
32
|
+
"Name of BOSH release within target BOSH. Default: 'appcloud'"
|
33
|
+
option "--release-version version", String,
|
34
|
+
"Version of target BOSH release within target BOSH. Default: 'latest'"
|
35
|
+
option "--stemcell-name name", String,
|
36
|
+
"Name of BOSH stemcell within target BOSH. Default: 'bosh-stemcell'"
|
37
|
+
option "--stemcell-version version", String,
|
38
|
+
"Version of BOSH stemcell within target BOSH. Default: determines latest for stemcell"
|
39
|
+
option "--admin-emails email1,email2", Array, "Admin email accounts in created CloudFoundry"
|
40
|
+
option "--skip-validations", "Skip all validations"
|
41
|
+
def prepare_system(name=nil)
|
42
|
+
setup_system_dir(name)
|
43
|
+
confirm_or_prompt_all_defaults
|
44
|
+
confirm_or_prompt_for_system_requirements
|
45
|
+
render_system
|
46
|
+
target_core_deployment_manifest
|
47
|
+
end
|
48
|
+
|
49
|
+
usage "cf change deas"
|
50
|
+
desc "change the number/flavor of DEA servers (servers that run CF apps)"
|
51
|
+
option "--flavor flavor", String, "Change flavor of all DEA servers"
|
52
|
+
def change_deas(server_count="1")
|
53
|
+
confirm_system
|
54
|
+
|
55
|
+
server_count = server_count.to_i # TODO nicer integer validation
|
56
|
+
if server_count <= 0
|
57
|
+
say "Additional server count (#{server_count}) was less that 1, defaulting to 1"
|
58
|
+
server_count = 1
|
59
|
+
end
|
60
|
+
|
61
|
+
server_flavor = options[:flavor]
|
62
|
+
unless non_interactive?
|
63
|
+
unless server_flavor
|
64
|
+
server_flavor = ask("Flavor of server for DEAs? ") do |q|
|
65
|
+
q.default = default_dea_server_flavor
|
66
|
+
end.to_s
|
67
|
+
end
|
68
|
+
end
|
69
|
+
unless server_flavor && server_flavor
|
70
|
+
err("Must provide server count and flavor values")
|
71
|
+
end
|
72
|
+
validate_compute_flavor(server_flavor)
|
73
|
+
|
74
|
+
dea_config = Bosh::CloudFoundry::Config::DeaConfig.build_from_system_config(system_config)
|
75
|
+
dea_config.update_count_and_flavor(server_count, server_flavor)
|
76
|
+
|
77
|
+
render_system
|
78
|
+
end
|
79
|
+
|
80
|
+
usage "cf add service"
|
81
|
+
desc "add additional CloudFoundry service node"
|
82
|
+
option "--flavor flavor", String, "Server flavor for additional service nodes"
|
83
|
+
def add_service_node(service_name, additional_count=1)
|
84
|
+
confirm_system
|
85
|
+
|
86
|
+
validate_service_name(service_name)
|
87
|
+
|
88
|
+
server_flavor = options[:flavor]
|
89
|
+
unless non_interactive?
|
90
|
+
unless server_flavor
|
91
|
+
server_flavor = ask("Flavor of server for #{service_name} service nodes? ") do |q|
|
92
|
+
q.default = default_service_server_flavor(service_name)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
unless server_flavor && server_flavor
|
97
|
+
err("Must provide server count and flavor values")
|
98
|
+
end
|
99
|
+
validate_compute_flavor(server_flavor)
|
100
|
+
|
101
|
+
service_config = service_config(service_name)
|
102
|
+
flavor_cluster = service_config.find_cluster_for_flavor(server_flavor) || {}
|
103
|
+
|
104
|
+
current_count = flavor_cluster["count"] || 0
|
105
|
+
server_count = current_count + additional_count.to_i # TODO nicer integer validation
|
106
|
+
say "Changing #{service_name} #{server_flavor} from #{current_count} to #{server_count}"
|
107
|
+
service_config.update_cluster_count_for_flavor(server_count, server_flavor)
|
108
|
+
|
109
|
+
render_system
|
110
|
+
end
|
111
|
+
|
112
|
+
usage "cf upload stemcell"
|
113
|
+
desc "download/create stemcell & upload to BOSH"
|
114
|
+
option "--latest", "Use latest stemcell; possibly not tagged stable"
|
115
|
+
option "--custom", "Create custom stemcell from BOSH git source"
|
116
|
+
def upload_stemcell
|
117
|
+
stemcell_type = "stable"
|
118
|
+
stemcell_type = "latest" if options[:latest]
|
119
|
+
stemcell_type = "custom" if options[:custom]
|
120
|
+
create_or_download_stemcell_then_upload(stemcell_type)
|
121
|
+
end
|
122
|
+
|
123
|
+
usage "cf upload release"
|
124
|
+
desc "fetch & upload latest public cloudfoundry release to BOSH"
|
125
|
+
option "--dev", "Create development release from very latest cf-release commits"
|
126
|
+
def upload_release
|
127
|
+
clone_or_update_cf_release
|
128
|
+
if options.delete(:dev)
|
129
|
+
create_and_upload_dev_release
|
130
|
+
else
|
131
|
+
upload_final_release
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
usage "cf merge gerrit"
|
136
|
+
desc "create development release including one or more gerrit patches"
|
137
|
+
def merge_gerrit(*gerrit_changes)
|
138
|
+
# gerrit_change might be:
|
139
|
+
# * refs/changes/84/13084/4
|
140
|
+
# * 84/13084/4
|
141
|
+
# * 'git pull http://reviews.cloudfoundry.org/cf-release refs/changes/84/13084/4'
|
142
|
+
gerrit_changes.each do |gerrit_change|
|
143
|
+
if refs_change = extract_refs_change(gerrit_change)
|
144
|
+
add_gerrit_refs_change(refs_change)
|
145
|
+
else
|
146
|
+
say "Please provide the gerrit change information in one of the following formats:".red
|
147
|
+
say " -> bosh cf merge gerrit refs/changes/84/13084/4"
|
148
|
+
say " -> bosh cf merge gerrit 84/13084/4"
|
149
|
+
say " -> bosh cf merge gerrit 'git pull http://reviews.cloudfoundry.org/cf-release refs/changes/84/13084/4'"
|
150
|
+
say ""
|
151
|
+
say "Please re-run the command again with the change reference formatted as above.".red
|
152
|
+
exit 1
|
153
|
+
end
|
154
|
+
end
|
155
|
+
apply_gerrit_patches
|
156
|
+
create_and_upload_dev_release
|
157
|
+
end
|
158
|
+
|
159
|
+
usage "cf deploy"
|
160
|
+
desc "deploy CloudFoundry system or apply any changes"
|
161
|
+
def deploy
|
162
|
+
confirm_system
|
163
|
+
Dir["#{system}/deployments/*.yml"].each do |deployment|
|
164
|
+
set_deployment(deployment)
|
165
|
+
bosh_cmd "deploy"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
usage "cf watch nats"
|
170
|
+
desc "subscribe to all nats messages within CloudFoundry"
|
171
|
+
def watch_nats
|
172
|
+
confirm_system
|
173
|
+
nats_props = deployment_manifest("core")["properties"]["nats"]
|
174
|
+
user, pass = nats_props["user"], nats_props["password"]
|
175
|
+
host, port = nats_props["address"], nats_props["port"]
|
176
|
+
nats_uri = "nats://#{user}:#{pass}@#{host}:#{port}"
|
177
|
+
sh "nats-sub '*.*' -s #{nats_uri}"
|
178
|
+
end
|
179
|
+
|
180
|
+
usage "cf show password"
|
181
|
+
desc "displays the common password for internal access"
|
182
|
+
def show_password
|
183
|
+
confirm_system
|
184
|
+
say system_config.common_password
|
185
|
+
end
|
186
|
+
|
187
|
+
# Creates initial system folder & targets that system folder
|
188
|
+
# The +system_config+ configuration does not work until
|
189
|
+
# a system folder is created and targeted so that a
|
190
|
+
# local configuration manifest can be stored (SystemConfig)
|
191
|
+
def setup_system_dir(name)
|
192
|
+
system_dir = File.join(base_systems_dir, name)
|
193
|
+
unless File.directory?(system_dir)
|
194
|
+
say "Creating new system #{name} directory"
|
195
|
+
mkdir_p(system_dir)
|
196
|
+
end
|
197
|
+
set_system(name)
|
198
|
+
end
|
199
|
+
|
200
|
+
# Set +system+ to specified name
|
201
|
+
def set_system(name)
|
202
|
+
system_dir = File.join(base_systems_dir, name)
|
203
|
+
unless File.directory?(system_dir)
|
204
|
+
err "CloudFoundry system path '#{system_dir.red}` does not exist"
|
205
|
+
end
|
206
|
+
|
207
|
+
say "CloudFoundry system set to #{system_dir.green}"
|
208
|
+
common_config.target_system = system_dir
|
209
|
+
common_config.save
|
210
|
+
end
|
211
|
+
|
212
|
+
def target_core_deployment_manifest
|
213
|
+
if deployment = Dir["#{system}/deployments/*-core.yml"].first
|
214
|
+
set_deployment(deployment)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Helper to tell the CLI to target a specific deployment manifest for the "bosh deploy" command
|
219
|
+
def set_deployment(path)
|
220
|
+
cmd = Bosh::Cli::Command::Deployment.new
|
221
|
+
cmd.set_current(path)
|
222
|
+
end
|
223
|
+
|
224
|
+
def confirm_bosh_target
|
225
|
+
return true if skip_validations?
|
226
|
+
if bosh_target && bosh_target_uuid
|
227
|
+
say("Current BOSH is '#{bosh_target.green}'")
|
228
|
+
else
|
229
|
+
err("BOSH target not set")
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def confirm_system
|
234
|
+
if system
|
235
|
+
say("Current CloudFoundry system is '#{system.green}'")
|
236
|
+
else
|
237
|
+
err("CloudFoundry system not set")
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# @return [String] label for the CPI being used by the target BOSH
|
242
|
+
# * "aws" - AWS
|
243
|
+
#
|
244
|
+
# Yet to be supported by bosh-cloudfoundry:
|
245
|
+
# * "openstack" - VMWare vSphere
|
246
|
+
# * "vsphere" - VMWare vSphere
|
247
|
+
# * "vcloud" - VMWare vCloud
|
248
|
+
def bosh_provider
|
249
|
+
if aws?
|
250
|
+
"aws"
|
251
|
+
else
|
252
|
+
err("Please implement cf.rb's bosh_provider for this IaaS")
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Deploying CloudFoundry to AWS?
|
257
|
+
# Is the target BOSH's IaaS using the AWS CPI?
|
258
|
+
# FIXME Currently only AWS is supported so its always AWS
|
259
|
+
def aws?
|
260
|
+
true
|
261
|
+
end
|
262
|
+
|
263
|
+
# User is prompted for common values at the
|
264
|
+
# start of a command rather than intermittently
|
265
|
+
# during a long-running command.
|
266
|
+
def confirm_or_prompt_all_defaults
|
267
|
+
confirm_bosh_target
|
268
|
+
cf_release_dir
|
269
|
+
stemcells_dir
|
270
|
+
base_systems_dir
|
271
|
+
end
|
272
|
+
|
273
|
+
# Assert that system configuration is available or prompt for values
|
274
|
+
def confirm_or_prompt_for_system_requirements
|
275
|
+
generate_generatable_options
|
276
|
+
validate_root_dns_maps_to_core_ip
|
277
|
+
ensure_security_group_prepared
|
278
|
+
validate_compute_flavor(core_server_flavor)
|
279
|
+
admin_emails
|
280
|
+
confirm_or_upload_release
|
281
|
+
confirm_or_upload_stemcell
|
282
|
+
end
|
283
|
+
|
284
|
+
# Confirms that the requested release name is
|
285
|
+
# already uploaded to BOSH, else
|
286
|
+
# proceeds to upload the release
|
287
|
+
def confirm_or_upload_release
|
288
|
+
switch_to_development_release if options.delete(:edge) || options.delete(:custom) || options.delete(:dev)
|
289
|
+
say "Using BOSH release name #{release_name_version} (#{effective_release_version})".green
|
290
|
+
unless bosh_release_names.include?(release_name)
|
291
|
+
say "BOSH does not contain release #{release_name.green}, uploading...".yellow
|
292
|
+
upload_release
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# Confirms that a stemcell has been uploaded
|
297
|
+
# and if so, determines its name/version.
|
298
|
+
# Otherwise, uploads the latest stable
|
299
|
+
# stemcell.
|
300
|
+
#
|
301
|
+
# At a more granular level:
|
302
|
+
# Are there any stemcells uploaded?
|
303
|
+
# If no, then upload one then set stemcell_version
|
304
|
+
# If there are stemcells
|
305
|
+
# If stemcell_version is set and its not in stemcell list
|
306
|
+
# then change stemcell_version to the latest stemcell
|
307
|
+
# Else if stemcell_version not set, then set to latest stemcell
|
308
|
+
def confirm_or_upload_stemcell
|
309
|
+
if stemcell_version
|
310
|
+
unless bosh_stemcell_versions.include?(stemcell_version)
|
311
|
+
say "Stemcell #{stemcell_name} #{stemcell_version} no longer exists on BOSH, choosing another..."
|
312
|
+
system_config.stemcell_version = nil
|
313
|
+
else
|
314
|
+
say "Using stemcell #{stemcell_name} #{stemcell_version}".green
|
315
|
+
return
|
316
|
+
end
|
317
|
+
end
|
318
|
+
unless latest_bosh_stemcell_version
|
319
|
+
if stemcell_name == DEFAULT_STEMCELL_NAME
|
320
|
+
say "Attempting to upload stemcell #{stemcell_name}..."
|
321
|
+
upload_stemcell
|
322
|
+
else
|
323
|
+
say "Please first upload stemcell #{stemcell_name} or change to default stemcell #{DEFAULT_STEMCELL_NAME}"
|
324
|
+
exit 1
|
325
|
+
end
|
326
|
+
end
|
327
|
+
unless stemcell_version && stemcell_version.size
|
328
|
+
system_config.stemcell_version = latest_bosh_stemcell_version
|
329
|
+
system_config.save
|
330
|
+
end
|
331
|
+
unless bosh_stemcell_versions.include?(stemcell_version)
|
332
|
+
say "Requested stemcell version #{stemcell_version} is not available.".yellow
|
333
|
+
system_config.stemcell_version = latest_bosh_stemcell_version
|
334
|
+
system_config.save
|
335
|
+
end
|
336
|
+
say "Using stemcell #{stemcell_name} #{stemcell_version}".green
|
337
|
+
end
|
338
|
+
|
339
|
+
|
340
|
+
def confirm_release_name
|
341
|
+
return true if skip_validations?
|
342
|
+
if release_name = options[:cf_release] || system_config.release_name
|
343
|
+
unless bosh_release_names.include?(release_name)
|
344
|
+
err("BOSH target #{bosh_target} does not have a release '#{release_name.red}'")
|
345
|
+
end
|
346
|
+
release_name
|
347
|
+
else
|
348
|
+
false
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# Largest version number BOSH stemcell ("bosh-stemcell")
|
353
|
+
# @return [String] version number, e.g. "0.6.7"
|
354
|
+
def latest_bosh_stemcell_version
|
355
|
+
@latest_bosh_stemcell_version ||= begin
|
356
|
+
if bosh_stemcell_versions.size > 0
|
357
|
+
say "Available BOSH stemcells '#{stemcell_name}': #{bosh_stemcell_versions.join(', ')}"
|
358
|
+
bosh_stemcell_versions.last
|
359
|
+
else
|
360
|
+
say "No stemcells '#{stemcell_name}' uploaded yet"
|
361
|
+
nil
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
# Creates/downloads a stemcell; then uploads it to target BOSH
|
367
|
+
# If +stemcell_type+ is "stable", then download the latest stemcell tagged "stable"
|
368
|
+
# If +stemcell_type+ is "latest", then download the latest stemcell, might not be "stable"
|
369
|
+
# If +stemcell_type+ is "custom", then create the stemcell from BOSH source
|
370
|
+
def create_or_download_stemcell_then_upload(stemcell_type)
|
371
|
+
confirm_bosh_target # fails if CLI is not targeting a BOSH
|
372
|
+
if stemcell_type.to_s == "custom"
|
373
|
+
create_custom_stemcell
|
374
|
+
validate_stemcell_created_successfully
|
375
|
+
stemcell_path = move_and_return_created_stemcell
|
376
|
+
else
|
377
|
+
stemcell_name = bosh_stemcell_name(stemcell_type)
|
378
|
+
stemcell_path = download_stemcell(stemcell_name)
|
379
|
+
end
|
380
|
+
upload_stemcell_to_bosh(stemcell_path)
|
381
|
+
end
|
382
|
+
|
383
|
+
# Creates a custom stemcell and copies it into +stemcells_dir+
|
384
|
+
# @return [String] path to the new stemcell file
|
385
|
+
def create_custom_stemcell
|
386
|
+
if generated_stemcell
|
387
|
+
say "Skipping stemcell creation as one sits in the tmp folder waiting patiently..."
|
388
|
+
else
|
389
|
+
say "Creating new stemcell for '#{bosh_provider.green}'..."
|
390
|
+
chdir(repos_dir) do
|
391
|
+
clone_or_update_repository("bosh", bosh_git_repo)
|
392
|
+
chdir("bosh") do
|
393
|
+
sh "bundle install --without development test"
|
394
|
+
sh "sudo bundle exec rake stemcell:basic['#{bosh_provider}']"
|
395
|
+
sh "sudo chown -R vcap:vcap /var/tmp/bosh/agent-*"
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
def generated_stemcell
|
402
|
+
@generated_stemcell ||= Dir['/var/tmp/bosh/agent-*/work/work/*.tgz'].first
|
403
|
+
end
|
404
|
+
|
405
|
+
def validate_stemcell_created_successfully
|
406
|
+
err "Stemcell was not created successfully" unless generated_stemcell
|
407
|
+
end
|
408
|
+
|
409
|
+
# Locates the newly created stemcell, moves it into +stemcells_dir+
|
410
|
+
# and returns the path of its final resting place
|
411
|
+
# @return [String] path to new stemcell file; or nil if no stemcell found
|
412
|
+
def move_and_return_created_stemcell
|
413
|
+
mv generated_stemcell, "#{stemcells_dir}/"
|
414
|
+
File.join(stemcells_dir, File.basename(generated_stemcell))
|
415
|
+
end
|
416
|
+
|
417
|
+
def clone_or_update_repository(name, repo_uri)
|
418
|
+
if File.directory?(name)
|
419
|
+
chdir(name) do
|
420
|
+
say "Updating #{name} repositry..."
|
421
|
+
sh "git pull origin master"
|
422
|
+
end
|
423
|
+
else
|
424
|
+
say "Cloning #{repo_uri} repositry..."
|
425
|
+
sh "git clone #{repo_uri} #{name}"
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
# The latest relevant public stemcell name
|
430
|
+
# Runs 'bosh public stemcells' and parses the output. Currently expects the output
|
431
|
+
# to look like:
|
432
|
+
# +-----------------------------------------+------------------------+
|
433
|
+
# | Name | Tags |
|
434
|
+
# +-----------------------------------------+------------------------+
|
435
|
+
# | bosh-stemcell-0.5.2.tgz | vsphere |
|
436
|
+
# | bosh-stemcell-aws-0.6.4.tgz | aws, stable |
|
437
|
+
# | bosh-stemcell-aws-0.6.7.tgz | aws |
|
438
|
+
def bosh_stemcell_name(stemcell_type)
|
439
|
+
tags = [bosh_provider]
|
440
|
+
tags << "stable" if stemcell_type == "stable"
|
441
|
+
bosh_stemcells_cmd = "bosh public stemcells --tags #{tags.join(',')}"
|
442
|
+
say "Locating bosh stemcell, running '#{bosh_stemcells_cmd}'..."
|
443
|
+
`#{bosh_stemcells_cmd} | grep ' bosh-stemcell-' | awk '{ print $2 }' | sort -r | head -n 1`.strip
|
444
|
+
end
|
445
|
+
|
446
|
+
def download_stemcell(stemcell_name)
|
447
|
+
mkdir_p(stemcells_dir)
|
448
|
+
chdir(stemcells_dir) do
|
449
|
+
if File.exists?(stemcell_name)
|
450
|
+
say "Stemcell #{stemcell_name} already downloaded".yellow
|
451
|
+
else
|
452
|
+
say "Downloading public stemcell #{stemcell_name}..."
|
453
|
+
bosh_cmd("download public stemcell #{stemcell_name}")
|
454
|
+
end
|
455
|
+
end
|
456
|
+
File.join(stemcells_dir, stemcell_name)
|
457
|
+
end
|
458
|
+
|
459
|
+
def upload_stemcell_to_bosh(stemcell_path)
|
460
|
+
say "Uploading stemcell located at #{stemcell_path}..."
|
461
|
+
bosh_cmd("upload stemcell #{stemcell_path}")
|
462
|
+
@bosh_stemcell_versions = nil # reset cache
|
463
|
+
end
|
464
|
+
|
465
|
+
# It is assumed that there is only one m
|
466
|
+
def validate_root_dns_maps_to_core_ip
|
467
|
+
core_ip # prompts if not already known
|
468
|
+
root_dns # prompts if not already known
|
469
|
+
|
470
|
+
validate_dns_a_record("api.#{root_dns}", core_ip)
|
471
|
+
validate_dns_a_record("demoapp.#{root_dns}", core_ip)
|
472
|
+
end
|
473
|
+
|
474
|
+
# Ensures that the security group exists
|
475
|
+
# and has the correct ports open
|
476
|
+
def ensure_security_group_prepared
|
477
|
+
provider.create_security_group(system_config.security_group, required_public_ports)
|
478
|
+
end
|
479
|
+
|
480
|
+
# TODO this could change based on jobs being included
|
481
|
+
def required_public_ports
|
482
|
+
{
|
483
|
+
ssh: 22,
|
484
|
+
http: 80,
|
485
|
+
https: 433,
|
486
|
+
postgres: 2544,
|
487
|
+
resque: 3456,
|
488
|
+
nats: 4222,
|
489
|
+
router: 8080,
|
490
|
+
# TODO serialization_data_server: 8090, - if NFS enabled
|
491
|
+
uaa: 8100
|
492
|
+
}
|
493
|
+
end
|
494
|
+
|
495
|
+
# Validates that +domain+ is an A record that resolves to +expected_ip_addresses+
|
496
|
+
# and no other IP addresses.
|
497
|
+
# * +expected_ip_addresses+ is a String (IPv4 address)
|
498
|
+
def validate_dns_a_record(domain, expected_ip_address)
|
499
|
+
return true if skip_validations?
|
500
|
+
say "Checking that DNS #{domain.green} resolves to IP address #{expected_ip_address.green}... ", " "
|
501
|
+
packet = Net::DNS::Resolver.start(domain, Net::DNS::A)
|
502
|
+
resolved_a_records = packet.answer.map(&:value)
|
503
|
+
if packet.answer.size == 0
|
504
|
+
error = "Domain '#{domain.green}' does not resolve to an IP address"
|
505
|
+
end
|
506
|
+
unless resolved_a_records == [expected_ip_address]
|
507
|
+
error = "Domain #{domain} should resolve to IP address #{expected_ip_address}"
|
508
|
+
end
|
509
|
+
if error
|
510
|
+
say "ooh no!".red
|
511
|
+
say "Please setup your DNS:"
|
512
|
+
say "Subdomain: * " + "(wildcard)".yellow
|
513
|
+
say "IP address: #{expected_ip_address}"
|
514
|
+
err(error)
|
515
|
+
else
|
516
|
+
say "ok".green
|
517
|
+
true
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
# Validates +server_size+ against the known list of instance types/server sizes
|
522
|
+
# for the target IaaS.
|
523
|
+
#
|
524
|
+
# For example, "m1.small" is a valid server size/instance type on all AWS regions
|
525
|
+
def validate_compute_flavor(flavor)
|
526
|
+
return true if skip_validations?
|
527
|
+
if aws?
|
528
|
+
unless aws_compute_flavors.select { |flavor| flavor[:id] == flavor }
|
529
|
+
err("Server flavor '#{flavor}' is not a valid AWS compute flavor")
|
530
|
+
end
|
531
|
+
else
|
532
|
+
err("Please implemenet cf.rb's validate_compute_flavor for this IaaS")
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
# If any system_config values that are needed are not provided,
|
537
|
+
# then ensure that a generated value is stored
|
538
|
+
def generate_generatable_options
|
539
|
+
common_password
|
540
|
+
if aws?
|
541
|
+
security_group
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
# Renders the +SystemConfig+ model (+system_config+) into the system's
|
546
|
+
# deployment manifest(s).
|
547
|
+
def render_system
|
548
|
+
renderer = Bosh::CloudFoundry::SystemDeploymentManifestRenderer.new(
|
549
|
+
system_config, common_config, config)
|
550
|
+
renderer.perform
|
551
|
+
end
|
552
|
+
|
553
|
+
def generate_dea_servers(server_count, server_flavor)
|
554
|
+
director_uuid = "DIRECTOR_UUID"
|
555
|
+
release_name = "appcloud"
|
556
|
+
stemcell_version = "0.6.4"
|
557
|
+
if aws?
|
558
|
+
resource_pool_cloud_properties = "instance_type: #{server_flavor}"
|
559
|
+
else
|
560
|
+
err("Please implemenet cf.rb's generate_dea_servers for this IaaS")
|
561
|
+
end
|
562
|
+
dea_max_memory = 2048 # FIXME a value based on server flavor RAM?
|
563
|
+
nats_password = "mynats1234"
|
564
|
+
system_dir = File.join(base_systems_dir, system_name)
|
565
|
+
mkdir_p(system_dir)
|
566
|
+
chdir system_dir do
|
567
|
+
require 'bosh-cloudfoundry/generators/dea_generator'
|
568
|
+
Bosh::CloudFoundry::Generators::DeaGenerator.start([
|
569
|
+
system_name,
|
570
|
+
server_count, server_flavor,
|
571
|
+
director_uuid, release_name, stemcell_version,
|
572
|
+
resource_pool_cloud_properties,
|
573
|
+
dea_max_memory,
|
574
|
+
nats_password])
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
# Valdiate that +service_name+ is a known, supported service name
|
579
|
+
def validate_service_name(service_name)
|
580
|
+
return true if skip_validations?
|
581
|
+
unless supported_services.include?(service_name)
|
582
|
+
supported_services_list = supported_services.join(", ")
|
583
|
+
err("Service '#{service_name}' is not a supported service, such as #{supported_services_list}")
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
def supported_services
|
588
|
+
%w[postgresql redis]
|
589
|
+
end
|
590
|
+
|
591
|
+
def service_config(service_name)
|
592
|
+
case service_name.to_sym
|
593
|
+
when :postgresql
|
594
|
+
Bosh::CloudFoundry::Config::PostgresqlServiceConfig.build_from_system_config(system_config)
|
595
|
+
when :redis
|
596
|
+
Bosh::CloudFoundry::Config::RedisServiceConfig.build_from_system_config(system_config)
|
597
|
+
else
|
598
|
+
raise "please add #{service_name} support to #service_config method"
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
def generate_service_servers(service_name, server_count, server_flavor)
|
603
|
+
director_uuid = "DIRECTOR_UUID"
|
604
|
+
release_name = "appcloud"
|
605
|
+
stemcell_version = "0.6.4"
|
606
|
+
if aws?
|
607
|
+
resource_pool_cloud_properties = "instance_type: #{server_flavor}"
|
608
|
+
else
|
609
|
+
err("Please implemenet cf.rb's generate_service_servers for this IaaS")
|
610
|
+
end
|
611
|
+
persistent_disk = 16192
|
612
|
+
nats_password = "mynats1234"
|
613
|
+
system_dir = File.join(base_systems_dir, system_name)
|
614
|
+
mkdir_p(system_dir)
|
615
|
+
chdir system_dir do
|
616
|
+
require 'bosh-cloudfoundry/generators/service_generator'
|
617
|
+
Bosh::CloudFoundry::Generators::ServiceGenerator.start([
|
618
|
+
system_name,
|
619
|
+
service_name, server_count, server_flavor,
|
620
|
+
director_uuid, release_name, stemcell_version,
|
621
|
+
resource_pool_cloud_properties, persistent_disk,
|
622
|
+
nats_password])
|
623
|
+
end
|
624
|
+
end
|
625
|
+
|
626
|
+
def default_core_server_flavor
|
627
|
+
if aws?
|
628
|
+
"m1.large"
|
629
|
+
else
|
630
|
+
err("Please implement cf.rb's default_core_server_flavor for this IaaS")
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
def default_dea_server_flavor
|
635
|
+
if aws?
|
636
|
+
"m1.large"
|
637
|
+
else
|
638
|
+
err("Please implement cf.rb's default_server_flavor for this IaaS")
|
639
|
+
end
|
640
|
+
end
|
641
|
+
|
642
|
+
def default_service_server_flavor(service_name)
|
643
|
+
if aws?
|
644
|
+
"m1.xlarge"
|
645
|
+
else
|
646
|
+
err("Please implement cf.rb's default_service_server_flavor for this IaaS")
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|
650
|
+
# @return [Array] of [Hash] for each supported compute flavor
|
651
|
+
# Example [Hash] { :bits => 0, :cores => 2, :disk => 0,
|
652
|
+
# :id => 't1.micro', :name => 'Micro Instance', :ram => 613}
|
653
|
+
def aws_compute_flavors
|
654
|
+
Fog::Compute::AWS::FLAVORS
|
655
|
+
end
|
656
|
+
|
657
|
+
# a helper object for the target BOSH provider
|
658
|
+
def provider
|
659
|
+
@provider ||= Bosh::CloudFoundry::Providers.for_bosh_provider_name(system_config)
|
660
|
+
end
|
661
|
+
|
662
|
+
def bosh_cmd(command)
|
663
|
+
full_command = "bosh -n --color #{command}"
|
664
|
+
sh full_command
|
665
|
+
end
|
666
|
+
|
667
|
+
end
|
668
|
+
end
|