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,6 @@
1
+ require "bosh-cloudfoundry/config/common_config"
2
+ require "bosh-cloudfoundry/config/system_config"
3
+ require "bosh-cloudfoundry/config/microbosh_config"
4
+ require "bosh-cloudfoundry/config/dea_config"
5
+ require "bosh-cloudfoundry/config/postgresql_service_config"
6
+ require "bosh-cloudfoundry/config/redis_service_config"
@@ -0,0 +1,27 @@
1
+ # Copyright (c) 2012-2013 Stark & Wayne, LLC
2
+
3
+ module Bosh; module CloudFoundry; module Config; end; end; end
4
+
5
+ module Bosh::CloudFoundry::Config
6
+ class CommonConfig < Bosh::Cli::Config
7
+
8
+ [
9
+ :base_systems_dir, # e.g. /var/vcap/store/systems
10
+ :target_system, # e.g. /var/vcap/store/systems/production
11
+ :cf_release_git_repo, # e.g. "git://github.com/cloudfoundry/cf-release.git"
12
+ :bosh_git_repo, # e.g. "git://github.com/cloudfoundry/bosh.git"
13
+ :releases_dir, # e.g. /var/vcap/store/releases
14
+ :cf_release_dir, # e.g. /var/vcap/store/releases/cf-release
15
+ :stemcells_dir, # e.g. /var/vcap/store/stemcells
16
+ :repos_dir, # e.g. /var/vcap/store/repos
17
+ ].each do |attr|
18
+ define_method attr do
19
+ read(attr, false)
20
+ end
21
+
22
+ define_method "#{attr}=" do |value|
23
+ write_global(attr, value)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,150 @@
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::DeaConfig
6
+ attr_reader :system_config
7
+
8
+ def initialize(system_config)
9
+ @system_config = system_config
10
+ end
11
+
12
+ def self.build_from_system_config(system_config)
13
+ system_config.dea ||= {}
14
+ new(system_config)
15
+ end
16
+
17
+ def bosh_provider_name
18
+ @system_config.bosh_provider
19
+ end
20
+
21
+ def update_count_and_flavor(server_count, server_flavor)
22
+ self.dea_server_count = server_count.to_i
23
+ self.dea_server_flavor = server_flavor.to_s
24
+ self.save
25
+ end
26
+
27
+ # Determine ow many DEA servers are required
28
+ # based on the system configuration
29
+ def dea_server_count
30
+ @system_config.dea["count"] || 0
31
+ end
32
+
33
+ def dea_server_count=(count)
34
+ @system_config.dea["count"] = count
35
+ end
36
+
37
+ def dea_server_flavor
38
+ @system_config.dea["flavor"]
39
+ end
40
+
41
+ def dea_server_flavor=(flavor)
42
+ @system_config.dea["flavor"] = flavor
43
+ end
44
+
45
+ def save
46
+ @system_config.save
47
+ end
48
+
49
+ # Adds additional cf-release jobs into the core server (the core job in the manifest)
50
+ def add_core_jobs_to_manifest(manifest)
51
+ if dea_server_count == 0
52
+ @core_job ||= manifest["jobs"].find { |job| job["name"] == "core" }
53
+ @core_job["template"] << "dea"
54
+ end
55
+ end
56
+
57
+ # Adds resource pools to the target manifest, if dea_server_count > 0
58
+ #
59
+ # - name: dea
60
+ # network: default
61
+ # size: 2
62
+ # stemcell:
63
+ # name: bosh-stemcell
64
+ # version: 0.6.4
65
+ # cloud_properties:
66
+ # instance_type: m1.xlarge
67
+ def add_resource_pools_to_manifest(manifest)
68
+ if dea_server_count > 0
69
+ resource_pool = {
70
+ "name" => "dea",
71
+ "network" => "default",
72
+ "size" => dea_server_count,
73
+ "stemcell" => {
74
+ "name" => @system_config.stemcell_name,
75
+ "version" => @system_config.stemcell_version
76
+ },
77
+ # TODO how to create "cloud_properties" per-provider?
78
+ "cloud_properties" => {
79
+ "instance_type" => dea_server_flavor
80
+ }
81
+ }
82
+ manifest["resource_pools"] << resource_pool
83
+ end
84
+ end
85
+
86
+ # Jobs to add to the target manifest
87
+ #
88
+ # - name: dea
89
+ # template:
90
+ # - dea
91
+ # instances: 2
92
+ # resource_pool: dea
93
+ # networks:
94
+ # - name: default
95
+ # default:
96
+ # - dns
97
+ # - gateway
98
+ def add_jobs_to_manifest(manifest)
99
+ if dea_server_count > 0
100
+ job = {
101
+ "name" => "dea",
102
+ "template" => ["dea"],
103
+ "instances" => dea_server_count,
104
+ "resource_pool" => "dea",
105
+ # TODO are these AWS-specific networks?
106
+ "networks" => [{
107
+ "name" => "default",
108
+ "default" => ["dns", "gateway"]
109
+ }]
110
+ }
111
+ manifest["jobs"] << job
112
+ end
113
+ end
114
+
115
+ # Add extra configuration properties into the manifest
116
+ # to configure the allocation of RAM to the DEA
117
+ def merge_manifest_properties(manifest)
118
+ manifest["properties"]["dea"] ||= {}
119
+ manifest["properties"]["dea"] = {
120
+ "max_memory" => max_memory
121
+ }
122
+ end
123
+
124
+ def max_memory
125
+ if dea_server_count > 0
126
+ max_memory_for_dedicated_dea
127
+ else
128
+ 512
129
+ end
130
+ end
131
+
132
+ # @return [Integer] available ram for running CloudFoundry apps
133
+ def max_memory_for_dedicated_dea
134
+ ram_for_server_flavor - preallocated_ram
135
+ end
136
+
137
+ # @return [Integer] the ballpark ram for DEA, BOSH agent, etc
138
+ def preallocated_ram
139
+ 300
140
+ end
141
+
142
+ def ram_for_server_flavor
143
+ provider.ram_for_server_flavor(dea_server_flavor)
144
+ end
145
+
146
+ # a helper object for the target BOSH provider
147
+ def provider
148
+ @provider ||= Bosh::CloudFoundry::Providers.for_bosh_provider_name(system_config)
149
+ end
150
+ end
@@ -0,0 +1,106 @@
1
+ # Copyright (c) 2012-2013 Stark & Wayne, LLC
2
+
3
+ module Bosh; module CloudFoundry; module Config; end; end; end
4
+
5
+ # Helper to create a Fog::Connection based on auto-discovery
6
+ # of the CPI credentials of a microbosh's local configuration.
7
+ #
8
+ # If ~/.bosh_deployer_config exists, it contains the location of
9
+ # the `micro_bosh.yml` for each microbosh.
10
+ class Bosh::CloudFoundry::Config::MicroboshConfig
11
+ attr_reader :deployed_microboshes, :target_bosh_host
12
+
13
+ def initialize(target_bosh_host)
14
+ @target_bosh_host = target_bosh_host
15
+ end
16
+
17
+ def valid?
18
+ validate_target_bosh_host &&
19
+ validate_bosh_deployer_config
20
+ end
21
+
22
+ def fog_compute
23
+ return nil unless valid?
24
+ @fog_compute ||= Fog::Compute.new(fog_connection_properties)
25
+ end
26
+
27
+ def fog_connection_properties
28
+ if aws?
29
+ ec2_endpoint = provider_credentials["ec2_endpoint"]
30
+ if ec2_endpoint =~ /ec2\.([\w-]+)\.amazonaws\.com/
31
+ region = $1
32
+ else
33
+ raise "please add support to extra 'region' from #{ec2_endpoint}"
34
+ end
35
+ {
36
+ provider: "aws",
37
+ region: region,
38
+ aws_access_key_id: provider_credentials["access_key_id"],
39
+ aws_secret_access_key: provider_credentials["secret_access_key"]
40
+ }
41
+ else
42
+ raise "please implement #fog_credentials for #{bosh_provider}"
43
+ end
44
+ end
45
+
46
+ def bosh_provider
47
+ cloud_config["plugin"]
48
+ end
49
+
50
+ def provider_credentials
51
+ cloud_config["properties"][bosh_provider]
52
+ end
53
+
54
+ def aws?
55
+ bosh_provider == "aws"
56
+ end
57
+
58
+ # micro_bosh.yml looks like:
59
+ #
60
+ # cloud:
61
+ # plugin: aws
62
+ # properties:
63
+ # aws:
64
+ # access_key_id: ACCESS
65
+ # secret_access_key: SECRET
66
+ # ec2_endpoint: ec2.us-east-1.amazonaws.com
67
+ # default_security_groups:
68
+ # - microbosh-aws-us-east-1
69
+ # default_key_name: microbosh-aws-us-east-1
70
+ # ec2_private_key: /home/vcap/.ssh/microbosh-aws-us-east-1.pem
71
+ def cloud_config
72
+ microbosh_config["cloud"]
73
+ end
74
+
75
+ def microbosh_config
76
+ YAML.load_file(target_microbosh_config_path)
77
+ end
78
+
79
+ # ensure input value is a valid URI
80
+ def validate_target_bosh_host
81
+ URI.parse(target_bosh_host)
82
+ true
83
+ rescue
84
+ false
85
+ end
86
+
87
+ # ensure there is a ~/.bosh_deployer_config
88
+ def validate_bosh_deployer_config
89
+ return false unless File.exist? bosh_deployer_config
90
+ @deployed_microboshes = YAML.load_file(bosh_deployer_config)
91
+ return false unless target_microbosh_config_path
92
+ true
93
+ end
94
+
95
+ def bosh_deployer_config
96
+ File.expand_path("~/.bosh_deployer_config")
97
+ end
98
+
99
+ # .bosh_deployer_config looks like:
100
+ #
101
+ # deployment:
102
+ # http://1.2.3.4:25555: /path/to/micro_bosh.yml
103
+ def target_microbosh_config_path
104
+ deployed_microboshes["deployment"][target_bosh_host]
105
+ end
106
+ end
@@ -0,0 +1,185 @@
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::PostgresqlServiceConfig
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.postgresql ||= []
13
+ new(system_config)
14
+ end
15
+
16
+ def config
17
+ @system_config.postgresql
18
+ end
19
+
20
+ # @return [Boolean] true if there are any postgresql 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
+ def update_cluster_count_for_flavor(server_count, server_flavor, server_plan="free")
34
+ if cluster = find_cluster_for_flavor(server_flavor.to_s)
35
+ cluster["count"] = server_count
36
+ else
37
+ config << {"count" => server_count, "flavor" => server_flavor.to_s, "plan" => server_plan}
38
+ end
39
+ self.save
40
+ end
41
+
42
+ # @return [Hash] the Hash from @system_config for the requested flavor
43
+ # nil if its not currently a requested flavor
44
+ def find_cluster_for_flavor(server_flavor)
45
+ @system_config.postgresql.find { |cl| cl["flavor"] == server_flavor }
46
+ end
47
+
48
+ def save
49
+ @system_config.save
50
+ end
51
+
52
+ # resource_pool name for a cluster
53
+ # cluster_name like 'postgresql_m1_xlarge_free'
54
+ def cluster_name(cluster)
55
+ server_flavor = cluster["flavor"]
56
+ server_plan = cluster["plan"] || "free"
57
+ cluster_name = "postgresql_#{server_flavor}_#{server_plan}"
58
+ cluster_name.gsub!(/\W+/, '_')
59
+ cluster_name
60
+ end
61
+
62
+ # Adds "postgresql_gateway" to colocated "core" job
63
+ def add_core_jobs_to_manifest(manifest)
64
+ if any_service_nodes?
65
+ @core_job ||= manifest["jobs"].find { |job| job["name"] == "core" }
66
+ @core_job["template"] << "postgresql_gateway"
67
+ end
68
+ end
69
+
70
+ # Adds resource pools to the target manifest, if server_count > 0
71
+ #
72
+ # - name: postgresql
73
+ # network: default
74
+ # size: 2
75
+ # stemcell:
76
+ # name: bosh-stemcell
77
+ # version: 0.6.4
78
+ # cloud_properties:
79
+ # instance_type: m1.xlarge
80
+ def add_resource_pools_to_manifest(manifest)
81
+ if any_service_nodes?
82
+ config.each do |cluster|
83
+ server_count = cluster["count"]
84
+ server_flavor = cluster["flavor"]
85
+ resource_pool = {
86
+ "name" => cluster_name(cluster),
87
+ "network" => "default",
88
+ "size" => server_count,
89
+ "stemcell" => {
90
+ "name" => @system_config.stemcell_name,
91
+ "version" => @system_config.stemcell_version
92
+ },
93
+ # TODO how to create "cloud_properties" per-provider?
94
+ "cloud_properties" => {
95
+ "instance_type" => server_flavor
96
+ },
97
+ "persistent_disk" => 16192
98
+ }
99
+ manifest["resource_pools"] << resource_pool
100
+ end
101
+ end
102
+ end
103
+
104
+ # Jobs to add to the target manifest
105
+ #
106
+ # - name: postgresql
107
+ # template:
108
+ # - postgresql
109
+ # instances: 2
110
+ # resource_pool: postgresql
111
+ # networks:
112
+ # - name: default
113
+ # default:
114
+ # - dns
115
+ # - gateway
116
+ def add_jobs_to_manifest(manifest)
117
+ if any_service_nodes?
118
+ config.each do |cluster|
119
+ server_count = cluster["count"]
120
+ server_flavor = cluster["flavor"]
121
+ job = {
122
+ "name" => cluster_name(cluster),
123
+ "template" => ["postgresql_node"],
124
+ "instances" => server_count,
125
+ "resource_pool" => cluster_name(cluster),
126
+ # TODO are these AWS-specific networks?
127
+ "networks" => [{
128
+ "name" => "default",
129
+ "default" => ["dns", "gateway"]
130
+ }],
131
+ "persistent_disk" => 16192
132
+ }
133
+ manifest["jobs"] << job
134
+ end
135
+ end
136
+ end
137
+
138
+ # Add extra configuration properties into the manifest
139
+ # for the gateway, node, and service plans
140
+ def merge_manifest_properties(manifest)
141
+ if any_service_nodes?
142
+ manifest["properties"]["postgresql_gateway"] = {
143
+ "admin_user"=>"psql_admin",
144
+ "admin_passwd_hash"=>nil,
145
+ "token"=>"TOKEN",
146
+ "supported_versions"=>["9.0"],
147
+ "version_aliases"=>{"current"=>"9.0"}
148
+ }
149
+ manifest["properties"]["postgresql_node"] = {
150
+ "admin_user"=>"psql_admin",
151
+ "admin_passwd_hash"=>nil,
152
+ "available_storage"=>2048,
153
+ "max_db_size"=>256,
154
+ "max_long_tx"=>30,
155
+ "supported_versions"=>["9.0"],
156
+ "default_version"=>"9.0"
157
+ }
158
+ manifest["properties"]["service_plans"]["postgresql"] = {
159
+ "free"=>
160
+ {"job_management"=>{"high_water"=>1400, "low_water"=>100},
161
+ "configuration"=>
162
+ {"capacity"=>200,
163
+ "max_db_size"=>128,
164
+ "max_long_query"=>3,
165
+ "max_long_tx"=>30,
166
+ "max_clients"=>20,
167
+ "backup"=>{"enable"=>true}}}
168
+ }
169
+ end
170
+ end
171
+
172
+ # @return [Integer] the ballpark ram for postgresql, BOSH agent, etc
173
+ def preallocated_ram
174
+ 300
175
+ end
176
+
177
+ def ram_for_server_flavor
178
+ provider.ram_for_server_flavor(server_flavor)
179
+ end
180
+
181
+ # a helper object for the target BOSH provider
182
+ def provider
183
+ @provider ||= Bosh::CloudFoundry::Providers.for_bosh_provider_name(system_config)
184
+ end
185
+ end