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