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