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