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