bosh-cloudfoundry 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +6 -0
  5. data/Guardfile +10 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +242 -0
  8. data/Rakefile +33 -0
  9. data/bosh-cloudfoundry.gemspec +29 -0
  10. data/config/defaults.yml +6 -0
  11. data/lib/bosh-cloudfoundry.rb +34 -0
  12. data/lib/bosh-cloudfoundry/bosh_release_manager.rb +141 -0
  13. data/lib/bosh-cloudfoundry/config.rb +6 -0
  14. data/lib/bosh-cloudfoundry/config/common_config.rb +27 -0
  15. data/lib/bosh-cloudfoundry/config/dea_config.rb +150 -0
  16. data/lib/bosh-cloudfoundry/config/microbosh_config.rb +106 -0
  17. data/lib/bosh-cloudfoundry/config/postgresql_service_config.rb +185 -0
  18. data/lib/bosh-cloudfoundry/config/redis_service_config.rb +179 -0
  19. data/lib/bosh-cloudfoundry/config/system_config.rb +60 -0
  20. data/lib/bosh-cloudfoundry/config_options.rb +362 -0
  21. data/lib/bosh-cloudfoundry/gerrit_patches_helper.rb +47 -0
  22. data/lib/bosh-cloudfoundry/providers.rb +19 -0
  23. data/lib/bosh-cloudfoundry/providers/aws.rb +75 -0
  24. data/lib/bosh-cloudfoundry/system_deployment_manifest_renderer.rb +286 -0
  25. data/lib/bosh-cloudfoundry/version.rb +5 -0
  26. data/lib/bosh/cli/commands/cf.rb +668 -0
  27. data/spec/assets/.gitkeep +0 -0
  28. data/spec/assets/cf-release/jobs/cloud_controller/templates/runtimes.yml +150 -0
  29. data/spec/assets/deployments/aws-core-1-m1.small-free-redis.yml +221 -0
  30. data/spec/assets/deployments/aws-core-1-m1.xlarge-free-postgresql-2-m1.small-free-postgresql.yml +248 -0
  31. data/spec/assets/deployments/aws-core-2-m1.xlarge-dea.yml +195 -0
  32. data/spec/assets/deployments/aws-core-only.yml +178 -0
  33. data/spec/assets/deployments/aws-core-with-nfs.yml +192 -0
  34. data/spec/functional/.gitkeep +0 -0
  35. data/spec/spec_helper.rb +41 -0
  36. data/spec/unit/.gitkeep +0 -0
  37. data/spec/unit/bosh_release_manager_spec.rb +36 -0
  38. data/spec/unit/cf_command_spec.rb +280 -0
  39. data/spec/unit/config/common_config_spec.rb +21 -0
  40. data/spec/unit/config/dea_config_spec.rb +92 -0
  41. data/spec/unit/config/microbosh_config_spec.rb +11 -0
  42. data/spec/unit/config/postgresql_service_config_spec.rb +103 -0
  43. data/spec/unit/config/redis_service_config_spec.rb +103 -0
  44. data/spec/unit/config/system_config_spec.rb +29 -0
  45. data/spec/unit/config_options_spec.rb +64 -0
  46. data/spec/unit/system_deployment_manifest_renderer_spec.rb +93 -0
  47. metadata +246 -0
@@ -0,0 +1,179 @@
1
+ # Copyright (c) 2012-2013 Stark & Wayne, LLC
2
+
3
+ module Bosh; module CloudFoundry; module Config; end; end; end
4
+
5
+ class Bosh::CloudFoundry::Config::RedisServiceConfig
6
+
7
+ def initialize(system_config)
8
+ @system_config = system_config
9
+ end
10
+
11
+ def self.build_from_system_config(system_config)
12
+ system_config.redis ||= []
13
+ new(system_config)
14
+ end
15
+
16
+ def config
17
+ @system_config.redis
18
+ end
19
+
20
+ # @return [Boolean] true if there are any redis nodes to be provisioned
21
+ def any_service_nodes?
22
+ total_service_nodes_count > 0
23
+ end
24
+
25
+ def total_service_nodes_count
26
+ config.inject(0) { |total, cluster| total + (cluster["count"].to_i) }
27
+ end
28
+
29
+ def bosh_provider_name
30
+ @system_config.bosh_provider
31
+ end
32
+
33
+ # Used by the CLI cf.rb to update +system_config+
34
+ def update_cluster_count_for_flavor(server_count, server_flavor, server_plan="free")
35
+ if cluster = find_cluster_for_flavor(server_flavor.to_s)
36
+ cluster["count"] = server_count
37
+ else
38
+ config << {"count" => server_count, "flavor" => server_flavor.to_s, "plan" => server_plan}
39
+ end
40
+ self.save
41
+ end
42
+
43
+ # @return [Hash] the Hash from @system_config for the requested flavor
44
+ # nil if its not currently a requested flavor
45
+ def find_cluster_for_flavor(server_flavor)
46
+ @system_config.redis.find { |cl| cl["flavor"] == server_flavor }
47
+ end
48
+
49
+ def save
50
+ @system_config.save
51
+ end
52
+
53
+ # resource_pool name for a cluster
54
+ # cluster_name like 'redis_m1_xlarge_free'
55
+ def cluster_name(cluster)
56
+ server_flavor = cluster["flavor"]
57
+ server_plan = cluster["plan"] || "free"
58
+ cluster_name = "redis_#{server_flavor}_#{server_plan}"
59
+ cluster_name.gsub!(/\W+/, '_')
60
+ cluster_name
61
+ end
62
+
63
+ # Adds "redis_gateway" to colocated "core" job
64
+ def add_core_jobs_to_manifest(manifest)
65
+ if any_service_nodes?
66
+ @core_job ||= manifest["jobs"].find { |job| job["name"] == "core" }
67
+ @core_job["template"] << "redis_gateway"
68
+ end
69
+ end
70
+
71
+ # Adds resource pools to the target manifest, if server_count > 0
72
+ #
73
+ # - name: redis
74
+ # network: default
75
+ # size: 2
76
+ # stemcell:
77
+ # name: bosh-stemcell
78
+ # version: 0.6.4
79
+ # cloud_properties:
80
+ # instance_type: m1.xlarge
81
+ def add_resource_pools_to_manifest(manifest)
82
+ if any_service_nodes?
83
+ config.each do |cluster|
84
+ server_count = cluster["count"]
85
+ server_flavor = cluster["flavor"]
86
+ resource_pool = {
87
+ "name" => cluster_name(cluster),
88
+ "network" => "default",
89
+ "size" => server_count,
90
+ "stemcell" => {
91
+ "name" => @system_config.stemcell_name,
92
+ "version" => @system_config.stemcell_version
93
+ },
94
+ # TODO how to create "cloud_properties" per-provider?
95
+ "cloud_properties" => {
96
+ "instance_type" => server_flavor
97
+ },
98
+ "persistent_disk" => @system_config.common_persistent_disk
99
+ }
100
+ manifest["resource_pools"] << resource_pool
101
+ end
102
+ end
103
+ end
104
+
105
+ # Jobs to add to the target manifest
106
+ #
107
+ # - name: redis
108
+ # template:
109
+ # - redis
110
+ # instances: 2
111
+ # resource_pool: redis
112
+ # networks:
113
+ # - name: default
114
+ # default:
115
+ # - dns
116
+ # - gateway
117
+ def add_jobs_to_manifest(manifest)
118
+ if any_service_nodes?
119
+ config.each do |cluster|
120
+ server_count = cluster["count"]
121
+ server_flavor = cluster["flavor"]
122
+ job = {
123
+ "name" => cluster_name(cluster),
124
+ "template" => ["redis_node"],
125
+ "instances" => server_count,
126
+ "resource_pool" => cluster_name(cluster),
127
+ # TODO are these AWS-specific networks?
128
+ "networks" => [{
129
+ "name" => "default",
130
+ "default" => ["dns", "gateway"]
131
+ }],
132
+ "persistent_disk" => @system_config.common_persistent_disk
133
+ }
134
+ manifest["jobs"] << job
135
+ end
136
+ end
137
+ end
138
+
139
+ # Add extra configuration properties into the manifest
140
+ # for the gateway, node, and service plans
141
+ def merge_manifest_properties(manifest)
142
+ if any_service_nodes?
143
+ manifest["properties"]["redis_gateway"] = {
144
+ "token"=>"TOKEN",
145
+ "supported_versions"=>["2.2"],
146
+ "version_aliases"=>{"current"=>"2.2"}
147
+ }
148
+ manifest["properties"]["redis_node"] = {
149
+ "available_memory"=>256,
150
+ "supported_versions"=>["2.2"],
151
+ "default_version"=>"2.2"
152
+ }
153
+ manifest["properties"]["service_plans"]["redis"] = {
154
+ "free"=>
155
+ {"job_management"=>{"high_water"=>1400, "low_water"=>100},
156
+ "configuration"=>
157
+ {"capacity"=>200,
158
+ "max_memory"=>16,
159
+ "max_swap"=>32,
160
+ "max_clients"=>500,
161
+ "backup"=>{"enable"=>true}}}
162
+ }
163
+ end
164
+ end
165
+
166
+ # @return [Integer] the ballpark ram for redis, BOSH agent, etc
167
+ def preallocated_ram
168
+ 300
169
+ end
170
+
171
+ def ram_for_server_flavor
172
+ provider.ram_for_server_flavor(server_flavor)
173
+ end
174
+
175
+ # a helper object for the target BOSH provider
176
+ def provider
177
+ @provider ||= Bosh::CloudFoundry::Providers.for_bosh_provider_name(system_config)
178
+ end
179
+ end
@@ -0,0 +1,60 @@
1
+ # Copyright (c) 2012-2013 Stark & Wayne, LLC
2
+
3
+ module Bosh; module CloudFoundry; module Config; end; end; end
4
+
5
+ # Model for the configuration data of a CloudFoundry System
6
+ # description.
7
+ # Stores the data as a YAML configuration file within a System's
8
+ # home folder.
9
+ class Bosh::CloudFoundry::Config::SystemConfig < Bosh::Cli::Config
10
+
11
+ # Defaults the +system_dir+ and +system_name+ based on the path
12
+ # of the +config_file+.
13
+ # It is assumed that the +config_file+ is located within a folder
14
+ # dedicated to the System begin configured.
15
+ def initialize(system_dir)
16
+ config_file = File.join(system_dir, "manifest.yml")
17
+ super(config_file, system_dir)
18
+ self.system_dir = system_dir
19
+ self.system_name = File.basename(system_dir)
20
+ end
21
+
22
+ [
23
+ :bosh_target, # e.g. http://1.2.3.4:25555
24
+ :bosh_target_uuid,
25
+ :bosh_provider, # from list 'aws', 'openstack', 'vsphere', 'vcloud'
26
+ :system_name, # e.g. production
27
+ :system_dir, # e.g. /var/vcap/store/systems/production
28
+ :release_name, # e.g. 'appcloud'
29
+ :release_version, # e.g. 'latest'
30
+ :gerrit_changes, # e.g. ['84/13084/4', '37/13137/4']
31
+ :stemcell_name, # e.g. 'bosh-stemcell'
32
+ :stemcell_version, # e.g. '0.6.7'
33
+ :core_ip, # Static IP for Core CF server (router, cc) e.g. '1.2.3.4'
34
+ :root_dns, # Root DNS for cc & user apps, e.g. 'mycompanycloud.com'
35
+ :core_server_flavor, # Server size for CF Core; e.g. 'm1.xlarge' on AWS
36
+ :runtimes, # e.g. { "ruby18" => false, "ruby19" => true }
37
+ :common_password, # e.g. 'c1oudc0wc1oudc0w` - must be 16 chars for CC password
38
+ :common_persistent_disk, # e.g. 16192 (integer in Mb)
39
+ :admin_emails, # e.g. ['drnic@starkandwayne.com']
40
+ :dea, # e.g. { "count" => 2, "flavor" => "m1.large" }
41
+ :postgresql, # e.g. [{ "count" => 2, "flavor" => "m1.large", "plan" => "free" }]
42
+ :redis, # e.g. [{ "count" => 2, "flavor" => "m1.large", "plan" => "free" }]
43
+ :security_group # e.g. "cloudfoundry-production"
44
+ ].each do |attr|
45
+ define_method attr do
46
+ read(attr, false)
47
+ end
48
+
49
+ define_method "#{attr}=" do |value|
50
+ write_global(attr, value)
51
+ end
52
+ end
53
+
54
+ def microbosh
55
+ unless bosh_target
56
+ raise "please set #bosh_target before requesting microbosh configuration"
57
+ end
58
+ @microbosh ||= Bosh::CloudFoundry::Config::MicroboshConfig.new(bosh_target)
59
+ end
60
+ end
@@ -0,0 +1,362 @@
1
+ # Copyright (c) 2012-2013 Stark & Wayne, LLC
2
+
3
+ module Bosh; module CloudFoundry; end; end
4
+
5
+ # A set of helper methods to add to a Command class to
6
+ # get values from Command +options+ if provided,
7
+ # else from +Bosh::CloudFoundry::Config+.
8
+ # Some helpers also prompt for values if
9
+ # no option/common_config value found.
10
+ #
11
+ # Assumes there is a +director+ method for making
12
+ # API calls to the target BOSH Director API.
13
+ module Bosh::CloudFoundry::ConfigOptions
14
+ DEFAULT_CONFIG_PATH = File.expand_path("~/.bosh_common_config")
15
+ DEFAULT_CF_RELEASE_GIT_REPO = "git://github.com/cloudfoundry/cf-release.git"
16
+ DEFAULT_BOSH_GIT_REPO = "git://github.com/cloudfoundry/bosh.git"
17
+ DEFAULT_BASE_SYSTEM_PATH = "/var/vcap/store/systems"
18
+ DEFAULT_RELEASES_PATH = "/var/vcap/store/releases"
19
+ DEFAULT_STEMCELLS_PATH = "/var/vcap/store/stemcells"
20
+ DEFAULT_REPOS_PATH = "/var/vcap/store/repos"
21
+ DEFAULT_RELEASE_NAME = "appcloud" # name of cf-release final release name
22
+ DEFAULT_RELEASE_VERSION = "124" # FIXME restore to "latest" when #49 fixed
23
+ DEFAULT_STEMCELL_NAME = "bosh-stemcell"
24
+ DEFAULT_COMMONT_PERSISTENT_DISK = 16192
25
+ COMMON_PASSWORD_SIZE = 16 # characters; the min for the CC password
26
+
27
+ # @return [Bosh::CloudFoundry::Config:: CommonConfig] Current common CF configuration
28
+ def common_config
29
+ @common_config ||= begin
30
+ config_file = options[:common_config] || DEFAULT_CONFIG_PATH
31
+ common_config = Bosh::CloudFoundry::Config:: CommonConfig.new(config_file)
32
+ common_config.cf_release_git_repo ||= DEFAULT_CF_RELEASE_GIT_REPO
33
+ common_config.bosh_git_repo ||= DEFAULT_BOSH_GIT_REPO
34
+ common_config.save
35
+ common_config
36
+ end
37
+ end
38
+
39
+ # @return [Bosh::CloudFoundry::Config::SystemConfig] System-specific configuration
40
+ def system_config
41
+ unless system
42
+ puts caller
43
+ err("Internal bug: cannot access system_config until a system has been selected by user")
44
+ end
45
+ @system_config ||= begin
46
+ system_config = Bosh::CloudFoundry::Config::SystemConfig.new(system)
47
+ system_config.bosh_target = options[:bosh_target] || config.target
48
+ system_config.bosh_target_uuid = options[:bosh_target_uuid] || config.target_uuid
49
+ system_config.bosh_provider = 'aws' # TODO support other BOSH providers
50
+ system_config.release_name ||= DEFAULT_RELEASE_NAME
51
+ system_config.release_version ||= DEFAULT_RELEASE_VERSION
52
+ system_config.stemcell_name ||= DEFAULT_STEMCELL_NAME
53
+ system_config.common_persistent_disk = DEFAULT_COMMONT_PERSISTENT_DISK
54
+ system_config.save
55
+ system_config
56
+ end
57
+ end
58
+
59
+ # @return [String] CloudFoundry system path
60
+ def system
61
+ options[:system] || common_config.target_system
62
+ end
63
+
64
+ # @return [String] CloudFoundry system name
65
+ def system_name
66
+ @system_name ||= system_config.system_name
67
+ end
68
+
69
+ # Example usage:
70
+ # overriddable_config_option :release_name, :system_config, :release_name
71
+ # overriddable_config_option :core_ip, :system_config
72
+ # overriddable_config_option :cf_release_git_repo, :common_config
73
+ #
74
+ # +target_config+ is a method name resolving to
75
+ # an instance of either +SystemConfig+ or +CommonConfig+.
76
+ def self.overriddable_config_option(config_option, target_config_accessor, target_config_name=nil)
77
+ target_config_name ||= config_option
78
+ config_option = config_option.to_sym
79
+ target_config_accessor = target_config_accessor.to_sym
80
+ target_config_name = target_config_name.to_sym
81
+ # The call:
82
+ # overriddable_config_option :release_name, :system_config, :release_name
83
+ # Creates the following method:
84
+ # def release_name
85
+ # if override = options[:release_name]
86
+ # system_config.release_name = override
87
+ # system_config.save
88
+ # end
89
+ # return system_config.release_name if system_config.release_name
90
+ # choose_release_name # if it exists; OR
91
+ # generate_release_name # if it exists; OR
92
+ # nil
93
+ # end
94
+ define_method config_option do
95
+ # convert :system_config into the instance of SystemConfig
96
+ target_config = self.send(target_config_accessor)
97
+ # determine if options has an override for
98
+ if override = options[config_option]
99
+ target_config.send(:"#{target_config_name}=", override)
100
+ target_config.save
101
+ end
102
+ config_value = target_config.send(target_config_name)
103
+ return config_value if config_value
104
+ if self.respond_to?(:"choose_#{config_option}")
105
+ self.send(:"choose_#{config_option}")
106
+ elsif self.respond_to?(:"generate_#{config_option}")
107
+ override = self.send(:"generate_#{config_option}")
108
+ target_config.send(:"#{target_config_name}=", override)
109
+ target_config.save
110
+ override
111
+ else
112
+ nil
113
+ end
114
+ end
115
+ end
116
+
117
+ # @return [String] BOSH target to manage CloudFoundry
118
+ overriddable_config_option :bosh_target, :system_config
119
+
120
+ # @return [String] BOSH target director UUID
121
+ overriddable_config_option :bosh_target_uuid, :system_config
122
+
123
+ # @return [String] Name of BOSH release in target BOSH
124
+ overriddable_config_option :release_name, :system_config
125
+
126
+ # @return [String] Version of BOSH release in target BOSH [defaulted above]
127
+ overriddable_config_option :release_version, :system_config
128
+
129
+ # @return [String] Name of BOSH stemcell to use for deployments
130
+ overriddable_config_option :stemcell_name, :system_config
131
+
132
+ # @return [String] Version of BOSH stemcell to use for deployments
133
+ overriddable_config_option :stemcell_version, :system_config
134
+
135
+ # @return [String] public IP address for the Core CF server (to the router)
136
+ overriddable_config_option :core_ip, :system_config
137
+
138
+ # @return [String] public DNS all apps & api access, e.g. mycompany.com
139
+ overriddable_config_option :root_dns, :system_config
140
+
141
+ # @return [String] flavor of server for the Core server in CloudFoundry deployment
142
+ overriddable_config_option :core_server_flavor, :system_config
143
+
144
+ # @return [Array] list of emails for pre-created admin accounts in CloudFoundry deployment
145
+ overriddable_config_option :admin_emails, :system_config
146
+
147
+ # @return [Integer] the persistent disk size (Mb) attached to any server that wants one
148
+ overriddable_config_option :common_persistent_disk, :system_config
149
+
150
+ # @return [String] a strong password used throughout deployment manifests
151
+ overriddable_config_option :common_password, :system_config
152
+
153
+ # @return [String] name of AWS security group being used
154
+ overriddable_config_option :security_group, :system_config
155
+
156
+ # @return [String] CloudFoundry BOSH release git URI
157
+ def cf_release_git_repo
158
+ options[:cf_release_git_repo] || common_config.cf_release_git_repo
159
+ end
160
+
161
+ # @return [String] Path to store BOSH release projects
162
+ def releases_dir
163
+ options[:releases_dir] || common_config.releases_dir || choose_releases_dir
164
+ end
165
+
166
+ # @return [String] Path to cf-release BOSH release
167
+ def cf_release_dir
168
+ options[:cf_release_dir] || common_config.cf_release_dir || begin
169
+ common_config.cf_release_dir = File.join(releases_dir, "cf-release")
170
+ common_config.save
171
+ common_config.cf_release_dir
172
+ end
173
+ end
174
+
175
+ # @return [String] Path to store stemcells locally
176
+ def stemcells_dir
177
+ options[:stemcells_dir] || common_config.stemcells_dir || choose_stemcells_dir
178
+ end
179
+
180
+ # @return [String] Path to store source repositories locally
181
+ def repos_dir
182
+ options[:repos_dir] || common_config.repos_dir || choose_repos_dir
183
+ end
184
+
185
+ # @return [Boolean] true if skipping validations
186
+ def skip_validations?
187
+ options[:no_validation] || options[:no_validations] || options[:skip_validations]
188
+ end
189
+
190
+ # @return [Boolean] true if release_version is 'latest'; or no system set yet
191
+ def use_latest_release?
192
+ system.nil? || release_version == "latest"
193
+ end
194
+
195
+ def bosh_git_repo
196
+ options[:bosh_git_repo] || common_config.bosh_git_repo
197
+ end
198
+
199
+ def deployment_manifest(subsystem="core")
200
+ YAML.load_file(deployment_manifest_path(subsystem))
201
+ end
202
+
203
+ def deployment_manifest_path(subsystem="core")
204
+ File.join(system, "deployments", "#{system_name}-#{subsystem}.yml")
205
+ end
206
+
207
+ # @return [String] Path to store BOSH systems (collections of deployments)
208
+ def base_systems_dir
209
+ @base_systems_dir ||= options[:base_systems_dir] || common_config.base_systems_dir || begin
210
+ if non_interactive?
211
+ err "Please set base_systems_dir configuration for non-interactive mode"
212
+ end
213
+
214
+ base_systems_dir = ask("Path for to store all CloudFoundry systems: ") {
215
+ |q| q.default = DEFAULT_BASE_SYSTEM_PATH }
216
+ common_config.base_systems_dir = File.expand_path(base_systems_dir)
217
+ unless File.directory?(common_config.base_systems_dir)
218
+ say "Creating systems path #{common_config.base_systems_dir}"
219
+ FileUtils.mkdir_p(common_config.base_systems_dir)
220
+ end
221
+ common_config.save
222
+ common_config.base_systems_dir
223
+ end
224
+ end
225
+
226
+ def choose_releases_dir
227
+ if non_interactive?
228
+ err "Please set releases_dir configuration for non-interactive mode"
229
+ end
230
+
231
+ releases_dir = ask("Path to store all BOSH releases: ") {
232
+ |q| q.default = DEFAULT_RELEASES_PATH }
233
+
234
+ common_config.releases_dir = File.expand_path(releases_dir)
235
+ unless File.directory?(common_config.releases_dir)
236
+ say "Creating releases path #{common_config.releases_dir}"
237
+ FileUtils.mkdir_p(common_config.releases_dir)
238
+ end
239
+ common_config.save
240
+ common_config.releases_dir
241
+ end
242
+
243
+ def choose_stemcells_dir
244
+ if non_interactive?
245
+ err "Please set stemcells_dir configuration for non-interactive mode"
246
+ end
247
+
248
+ stemcells_dir = ask("Path to store downloaded/created stemcells: ") {
249
+ |q| q.default = DEFAULT_STEMCELLS_PATH }
250
+
251
+ common_config.stemcells_dir = File.expand_path(stemcells_dir)
252
+ unless File.directory?(common_config.stemcells_dir)
253
+ say "Creating stemcells path #{common_config.stemcells_dir}"
254
+ FileUtils.mkdir_p(common_config.stemcells_dir)
255
+ end
256
+ common_config.save
257
+ common_config.stemcells_dir
258
+ end
259
+
260
+ def choose_repos_dir
261
+ if non_interactive?
262
+ err "Please set repos_dir configuration for non-interactive mode"
263
+ end
264
+
265
+ repos_dir = ask("Path to store source repositories: ") {
266
+ |q| q.default = DEFAULT_REPOS_PATH }
267
+
268
+ common_config.repos_dir = File.expand_path(repos_dir)
269
+ unless File.directory?(common_config.repos_dir)
270
+ say "Creating repos path #{common_config.repos_dir}"
271
+ FileUtils.mkdir_p(common_config.repos_dir)
272
+ end
273
+ common_config.save
274
+ common_config.repos_dir
275
+ end
276
+
277
+ # @return [String] Primary static IP for CloudController & Router
278
+ def choose_core_ip
279
+ if non_interactive?
280
+ err "Please set core_ip configuration for non-interactive mode"
281
+ end
282
+
283
+ if aws?
284
+ system_config.core_ip = ask("Main public IP address (press Enter to provision new IP): ").to_s
285
+ else
286
+ system_config.core_ip = ask("Main public IP address: ").to_s
287
+ end
288
+ if system_config.core_ip.blank?
289
+ say "Provisioning #{bosh_provider} public IP address..."
290
+ system_config.core_ip = provider.provision_public_ip_address
291
+ if system_config.core_ip.blank?
292
+ say "Hmmm, I wasn't able to get a public IP at the moment. Perhaps try again or provision it manually?".red
293
+ exit 1
294
+ end
295
+ end
296
+ system_config.save
297
+ system_config.core_ip
298
+ end
299
+
300
+ # @return [String] Root DNS for applications & CloudController API
301
+ def choose_root_dns
302
+ if non_interactive?
303
+ err "Please set root_dns configuration for non-interactive mode"
304
+ end
305
+
306
+ system_config.root_dns = ask("Root DNS (e.g. mycompany.com): ").to_s
307
+ system_config.save
308
+ system_config.root_dns
309
+ end
310
+
311
+ def choose_core_server_flavor
312
+ if non_interactive?
313
+ err "Please set core_server_flavor configuration for non-interactive mode"
314
+ end
315
+
316
+ server_flavor = ask("Server flavor for core of CloudFoundry? ") do |q|
317
+ q.default = default_core_server_flavor
318
+ end
319
+ system_config.core_server_flavor = server_flavor.to_s
320
+ system_config.save
321
+ system_config.core_server_flavor
322
+ end
323
+
324
+ def choose_admin_emails
325
+ if non_interactive?
326
+ err "Please set admin_emails configuration for non-interactive mode"
327
+ end
328
+
329
+ admin_email_list = ask("Email address for administrator of CloudFoundry? ") do |q|
330
+ git_email = `git config user.email`.strip
331
+ q.default = git_email if git_email.size > 0
332
+ end
333
+ admin_emails = admin_email_list.to_s.split(",")
334
+ system_config.admin_emails = admin_emails
335
+ system_config.save
336
+ system_config.admin_emails
337
+ end
338
+
339
+ # generates a password of a specific length; defaults to size +COMMON_PASSWORD_SIZE+
340
+ def generate_common_password(size=COMMON_PASSWORD_SIZE)
341
+ OpenSSL::Random.random_bytes(size).unpack("H*")[0][0..size-1]
342
+ end
343
+
344
+ def generate_security_group
345
+ "cloudfoundry-#{system_name}"
346
+ end
347
+
348
+ # List of versions of stemcell called "bosh-stemcell" that are available
349
+ # in target BOSH.
350
+ # Ordered by version number.
351
+ # @return [Array] BOSH stemcell versions available in target BOSH, e.g. ["0.6.4", "0.6.7"]
352
+ def bosh_stemcell_versions
353
+ @bosh_stemcell_versions ||= begin
354
+ # [{"name"=>"bosh-stemcell", "version"=>"0.6.7", "cid"=>"ami-9730bffe"}]
355
+ stemcells = director.list_stemcells
356
+ stemcells.select! {|s| s["name"] == stemcell_name}
357
+ stemcells.map { |rel| rel["version"] }.sort { |v1, v2|
358
+ version_cmp(v1, v2)
359
+ }
360
+ end
361
+ end
362
+ end