bosh_cli_plugin_micro 1.5.0.pre.1113
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.
- data/README.rdoc +145 -0
- data/config/aws_defaults.yml +53 -0
- data/config/openstack_defaults.yml +52 -0
- data/config/vcloud_defaults.yml +41 -0
- data/config/vsphere_defaults.yml +44 -0
- data/lib/bosh/cli/commands/micro.rb +442 -0
- data/lib/deployer/config.rb +157 -0
- data/lib/deployer/helpers.rb +115 -0
- data/lib/deployer/instance_manager/aws.rb +181 -0
- data/lib/deployer/instance_manager/openstack.rb +174 -0
- data/lib/deployer/instance_manager/vcloud.rb +41 -0
- data/lib/deployer/instance_manager/vsphere.rb +46 -0
- data/lib/deployer/instance_manager.rb +555 -0
- data/lib/deployer/models/instance.rb +6 -0
- data/lib/deployer/specification.rb +97 -0
- data/lib/deployer/version.rb +7 -0
- data/lib/deployer.rb +23 -0
- metadata +225 -0
@@ -0,0 +1,181 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Deployer
|
4
|
+
class InstanceManager
|
5
|
+
|
6
|
+
class Aws < InstanceManager
|
7
|
+
|
8
|
+
def update_spec(spec)
|
9
|
+
properties = spec.properties
|
10
|
+
|
11
|
+
# pick from micro_bosh.yml the aws settings in `apply_spec` section (apply_spec.properties.aws),
|
12
|
+
# and if it doesn't exist, use the bosh deployer aws properties (cloud.properties.aws)
|
13
|
+
properties["aws"] = Config.spec_properties["aws"] || Config.cloud_options["properties"]["aws"].dup
|
14
|
+
|
15
|
+
properties["aws"]["registry"] = Config.cloud_options["properties"]["registry"]
|
16
|
+
properties["aws"]["stemcell"] = Config.cloud_options["properties"]["stemcell"]
|
17
|
+
|
18
|
+
spec.delete("networks")
|
19
|
+
end
|
20
|
+
|
21
|
+
def configure
|
22
|
+
properties = Config.cloud_options["properties"]
|
23
|
+
@ssh_user = properties["aws"]["ssh_user"]
|
24
|
+
@ssh_port = properties["aws"]["ssh_port"] || 22
|
25
|
+
@ssh_wait = properties["aws"]["ssh_wait"] || 60
|
26
|
+
|
27
|
+
key = properties["aws"]["ec2_private_key"]
|
28
|
+
err "Missing properties.aws.ec2_private_key" unless key
|
29
|
+
@ssh_key = File.expand_path(key)
|
30
|
+
unless File.exists?(@ssh_key)
|
31
|
+
err "properties.aws.ec2_private_key '#{key}' does not exist"
|
32
|
+
end
|
33
|
+
|
34
|
+
uri = URI.parse(properties["registry"]["endpoint"])
|
35
|
+
user, password = uri.userinfo.split(":", 2)
|
36
|
+
@registry_port = uri.port
|
37
|
+
|
38
|
+
@registry_db = Tempfile.new("bosh_registry_db")
|
39
|
+
|
40
|
+
@registry_connection_settings = {
|
41
|
+
'adapter' => 'sqlite',
|
42
|
+
'database' => @registry_db.path
|
43
|
+
}
|
44
|
+
|
45
|
+
registry_config = {
|
46
|
+
"logfile" => "./bosh-registry.log",
|
47
|
+
"http" => {
|
48
|
+
"port" => uri.port,
|
49
|
+
"user" => user,
|
50
|
+
"password" => password
|
51
|
+
},
|
52
|
+
"db" => @registry_connection_settings,
|
53
|
+
"cloud" => {
|
54
|
+
"plugin" => "aws",
|
55
|
+
"aws" => properties["aws"]
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
@registry_config = Tempfile.new("bosh_registry_yml")
|
60
|
+
@registry_config.write(Psych.dump(registry_config))
|
61
|
+
@registry_config.close
|
62
|
+
end
|
63
|
+
|
64
|
+
def start
|
65
|
+
configure()
|
66
|
+
|
67
|
+
Sequel.connect(@registry_connection_settings) do |db|
|
68
|
+
migrate(db)
|
69
|
+
instances = @deployments["registry_instances"]
|
70
|
+
db[:registry_instances].insert_multiple(instances) if instances
|
71
|
+
end
|
72
|
+
|
73
|
+
unless has_bosh_registry?
|
74
|
+
err "bosh-registry command not found - " +
|
75
|
+
"run 'gem install bosh-registry'"
|
76
|
+
end
|
77
|
+
|
78
|
+
cmd = "bosh-registry -c #{@registry_config.path}"
|
79
|
+
|
80
|
+
@registry_pid = spawn(cmd)
|
81
|
+
|
82
|
+
5.times do
|
83
|
+
sleep 0.5
|
84
|
+
if Process.waitpid(@registry_pid, Process::WNOHANG)
|
85
|
+
err "`#{cmd}` failed, exit status=#{$?.exitstatus}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
timeout_time = Time.now.to_f + (60 * 5)
|
90
|
+
http_client = HTTPClient.new()
|
91
|
+
begin
|
92
|
+
http_client.head("http://127.0.0.1:#{@registry_port}")
|
93
|
+
sleep 0.5
|
94
|
+
rescue URI::Error, SocketError, Errno::ECONNREFUSED, HTTPClient::ReceiveTimeoutError => e
|
95
|
+
if timeout_time - Time.now.to_f > 0
|
96
|
+
retry
|
97
|
+
else
|
98
|
+
err "Cannot access bosh-registry: #{e.message}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
logger.info("bosh-registry is ready on port #{@registry_port}")
|
103
|
+
ensure
|
104
|
+
@registry_config.unlink if @registry_config
|
105
|
+
end
|
106
|
+
|
107
|
+
def stop
|
108
|
+
if @registry_pid && process_exists?(@registry_pid)
|
109
|
+
Process.kill("INT", @registry_pid)
|
110
|
+
Process.waitpid(@registry_pid)
|
111
|
+
end
|
112
|
+
|
113
|
+
return unless @registry_connection_settings
|
114
|
+
|
115
|
+
Sequel.connect(@registry_connection_settings) do |db|
|
116
|
+
@deployments["registry_instances"] = db[:registry_instances].map {|row| row}
|
117
|
+
end
|
118
|
+
|
119
|
+
save_state
|
120
|
+
@registry_db.unlink if @registry_db
|
121
|
+
end
|
122
|
+
|
123
|
+
def discover_bosh_ip
|
124
|
+
if exists?
|
125
|
+
# choose elastic IP over public, as any agent connecting to the
|
126
|
+
# deployed micro bosh will be cut off from the public IP when
|
127
|
+
# we re-deploy micro bosh
|
128
|
+
if cloud.ec2.instances[state.vm_cid].has_elastic_ip?
|
129
|
+
ip = cloud.ec2.instances[state.vm_cid].elastic_ip.public_ip
|
130
|
+
else
|
131
|
+
ip = cloud.ec2.instances[state.vm_cid].public_ip_address
|
132
|
+
end
|
133
|
+
|
134
|
+
if ip && ip != Config.bosh_ip
|
135
|
+
Config.bosh_ip = ip
|
136
|
+
logger.info("discovered bosh ip=#{Config.bosh_ip}")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
super
|
141
|
+
end
|
142
|
+
|
143
|
+
def service_ip
|
144
|
+
cloud.ec2.instances[state.vm_cid].private_ip_address
|
145
|
+
end
|
146
|
+
|
147
|
+
# @return [Integer] size in MiB
|
148
|
+
def disk_size(cid)
|
149
|
+
# AWS stores disk size in GiB but the CPI uses MiB
|
150
|
+
cloud.ec2.volumes[cid].size * 1024
|
151
|
+
end
|
152
|
+
|
153
|
+
def persistent_disk_changed?
|
154
|
+
# since AWS stores disk size in GiB and the CPI uses MiB there
|
155
|
+
# is a risk of conversion errors which lead to an unnecessary
|
156
|
+
# disk migration, so we need to do a double conversion
|
157
|
+
# here to avoid that
|
158
|
+
requested = (Config.resources['persistent_disk'] / 1024.0).ceil * 1024
|
159
|
+
requested != disk_size(state.disk_cid)
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def has_bosh_registry?(path=ENV['PATH'])
|
165
|
+
path.split(":").each do |dir|
|
166
|
+
return true if File.exist?(File.join(dir, "bosh-registry"))
|
167
|
+
end
|
168
|
+
false
|
169
|
+
end
|
170
|
+
|
171
|
+
def migrate(db)
|
172
|
+
db.create_table :registry_instances do
|
173
|
+
primary_key :id
|
174
|
+
column :instance_id, :text, :unique => true, :null => false
|
175
|
+
column :settings, :text, :null => false
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Deployer
|
4
|
+
class InstanceManager
|
5
|
+
|
6
|
+
class Openstack < InstanceManager
|
7
|
+
|
8
|
+
def update_spec(spec)
|
9
|
+
properties = spec.properties
|
10
|
+
|
11
|
+
properties["openstack"] =
|
12
|
+
Config.spec_properties["openstack"] ||
|
13
|
+
Config.cloud_options["properties"]["openstack"].dup
|
14
|
+
|
15
|
+
properties["openstack"]["registry"] = Config.cloud_options["properties"]["registry"]
|
16
|
+
properties["openstack"]["stemcell"] = Config.cloud_options["properties"]["stemcell"]
|
17
|
+
|
18
|
+
spec.delete("networks")
|
19
|
+
end
|
20
|
+
|
21
|
+
def configure
|
22
|
+
properties = Config.cloud_options["properties"]
|
23
|
+
@ssh_user = properties["openstack"]["ssh_user"]
|
24
|
+
@ssh_port = properties["openstack"]["ssh_port"] || 22
|
25
|
+
@ssh_wait = properties["openstack"]["ssh_wait"] || 60
|
26
|
+
|
27
|
+
key = properties["openstack"]["private_key"]
|
28
|
+
err "Missing properties.openstack.private_key" unless key
|
29
|
+
@ssh_key = File.expand_path(key)
|
30
|
+
unless File.exists?(@ssh_key)
|
31
|
+
err "properties.openstack.private_key '#{key}' does not exist"
|
32
|
+
end
|
33
|
+
|
34
|
+
uri = URI.parse(properties["registry"]["endpoint"])
|
35
|
+
user, password = uri.userinfo.split(":", 2)
|
36
|
+
@registry_port = uri.port
|
37
|
+
|
38
|
+
@registry_db = Tempfile.new("bosh_registry_db")
|
39
|
+
@registry_connection_settings = {
|
40
|
+
'adapter' => 'sqlite',
|
41
|
+
'database' => @registry_db.path
|
42
|
+
}
|
43
|
+
|
44
|
+
registry_config = {
|
45
|
+
"logfile" => "./bosh-registry.log",
|
46
|
+
"http" => {
|
47
|
+
"port" => uri.port,
|
48
|
+
"user" => user,
|
49
|
+
"password" => password
|
50
|
+
},
|
51
|
+
"db" => @registry_connection_settings,
|
52
|
+
"cloud" => {
|
53
|
+
"plugin" => "openstack",
|
54
|
+
"openstack" => properties["openstack"]
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
@registry_config = Tempfile.new("bosh_registry_yml")
|
59
|
+
@registry_config.write(Psych.dump(registry_config))
|
60
|
+
@registry_config.close
|
61
|
+
end
|
62
|
+
|
63
|
+
def start
|
64
|
+
configure()
|
65
|
+
|
66
|
+
Sequel.connect(@registry_connection_settings) do |db|
|
67
|
+
migrate(db)
|
68
|
+
instances = @deployments["registry_instances"]
|
69
|
+
db[:registry_instances].insert_multiple(instances) if instances
|
70
|
+
end
|
71
|
+
|
72
|
+
unless has_bosh_registry?
|
73
|
+
err "bosh-registry command not found - " +
|
74
|
+
"run 'gem install bosh-registry'"
|
75
|
+
end
|
76
|
+
|
77
|
+
cmd = "bosh-registry -c #{@registry_config.path}"
|
78
|
+
|
79
|
+
@registry_pid = spawn(cmd)
|
80
|
+
|
81
|
+
5.times do
|
82
|
+
sleep 0.5
|
83
|
+
if Process.waitpid(@registry_pid, Process::WNOHANG)
|
84
|
+
err "`#{cmd}` failed, exit status=#{$?.exitstatus}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
timeout_time = Time.now.to_f + (60 * 5)
|
89
|
+
http_client = HTTPClient.new()
|
90
|
+
begin
|
91
|
+
http_client.head("http://127.0.0.1:#{@registry_port}")
|
92
|
+
sleep 0.5
|
93
|
+
rescue URI::Error, SocketError, Errno::ECONNREFUSED, HTTPClient::ReceiveTimeoutError => e
|
94
|
+
if timeout_time - Time.now.to_f > 0
|
95
|
+
retry
|
96
|
+
else
|
97
|
+
err "Cannot access bosh-registry: #{e.message}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
logger.info("bosh-registry is ready on port #{@registry_port}")
|
102
|
+
ensure
|
103
|
+
@registry_config.unlink if @registry_config
|
104
|
+
end
|
105
|
+
|
106
|
+
def stop
|
107
|
+
if @registry_pid && process_exists?(@registry_pid)
|
108
|
+
Process.kill("INT", @registry_pid)
|
109
|
+
Process.waitpid(@registry_pid)
|
110
|
+
end
|
111
|
+
|
112
|
+
return unless @registry_connection_settings
|
113
|
+
|
114
|
+
Sequel.connect(@registry_connection_settings) do |db|
|
115
|
+
@deployments["registry_instances"] = db[:registry_instances].map {|row| row}
|
116
|
+
end
|
117
|
+
|
118
|
+
save_state
|
119
|
+
@registry_db.unlink if @registry_db
|
120
|
+
end
|
121
|
+
|
122
|
+
def discover_bosh_ip
|
123
|
+
if exists?
|
124
|
+
floating_ip = cloud.openstack.servers.get(state.vm_cid).floating_ip_address
|
125
|
+
ip = floating_ip || service_ip
|
126
|
+
|
127
|
+
if ip != Config.bosh_ip
|
128
|
+
Config.bosh_ip = ip
|
129
|
+
logger.info("discovered bosh ip=#{Config.bosh_ip}")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
super
|
134
|
+
end
|
135
|
+
|
136
|
+
def service_ip
|
137
|
+
cloud.openstack.servers.get(state.vm_cid).private_ip_address
|
138
|
+
end
|
139
|
+
|
140
|
+
# @return [Integer] size in MiB
|
141
|
+
def disk_size(cid)
|
142
|
+
# OpenStack stores disk size in GiB but we work with MiB
|
143
|
+
cloud.openstack.volumes.get(cid).size * 1024
|
144
|
+
end
|
145
|
+
|
146
|
+
def persistent_disk_changed?
|
147
|
+
# since OpenStack stores disk size in GiB and we use MiB there
|
148
|
+
# is a risk of conversion errors which lead to an unnecessary
|
149
|
+
# disk migration, so we need to do a double conversion
|
150
|
+
# here to avoid that
|
151
|
+
requested = (Config.resources['persistent_disk'] / 1024.0).ceil * 1024
|
152
|
+
requested != disk_size(state.disk_cid)
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
def has_bosh_registry?(path=ENV['PATH'])
|
158
|
+
path.split(":").each do |dir|
|
159
|
+
return true if File.exist?(File.join(dir, "bosh-registry"))
|
160
|
+
end
|
161
|
+
false
|
162
|
+
end
|
163
|
+
|
164
|
+
def migrate(db)
|
165
|
+
db.create_table :registry_instances do
|
166
|
+
primary_key :id
|
167
|
+
column :instance_id, :text, :unique => true, :null => false
|
168
|
+
column :settings, :text, :null => false
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Deployer
|
4
|
+
class InstanceManager
|
5
|
+
|
6
|
+
class Vcloud < InstanceManager
|
7
|
+
|
8
|
+
|
9
|
+
def remote_tunnel(port)
|
10
|
+
# VCloud / vsphere does not use bosh-registry so no remote_tunnel
|
11
|
+
# to bosh-registry is required
|
12
|
+
end
|
13
|
+
|
14
|
+
def update_spec(spec)
|
15
|
+
properties = spec.properties
|
16
|
+
|
17
|
+
properties["vcd"] =
|
18
|
+
Config.spec_properties["vcd"] ||
|
19
|
+
Config.cloud_options["properties"]["vcds"].first.dup
|
20
|
+
|
21
|
+
properties["vcd"]["address"] ||= properties["vcd"]["url"]
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Integer] size in MiB
|
25
|
+
def disk_size(cid)
|
26
|
+
cloud.get_disk_size_mb(cid)
|
27
|
+
end
|
28
|
+
|
29
|
+
def persistent_disk_changed?
|
30
|
+
Config.resources['persistent_disk'] != disk_size(state.disk_cid)
|
31
|
+
end
|
32
|
+
|
33
|
+
def check_dependencies
|
34
|
+
if Bosh::Common.which(%w[genisoimage mkisofs]).nil?
|
35
|
+
err("either of 'genisoimage' or 'mkisofs' commands must be present")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Deployer
|
4
|
+
class InstanceManager
|
5
|
+
|
6
|
+
class Vsphere < InstanceManager
|
7
|
+
|
8
|
+
def remote_tunnel(port)
|
9
|
+
end
|
10
|
+
|
11
|
+
def disk_model
|
12
|
+
if @disk_model.nil?
|
13
|
+
require "cloud/vsphere"
|
14
|
+
@disk_model = VSphereCloud::Models::Disk
|
15
|
+
end
|
16
|
+
@disk_model
|
17
|
+
end
|
18
|
+
|
19
|
+
def update_spec(spec)
|
20
|
+
properties = spec.properties
|
21
|
+
|
22
|
+
properties["vcenter"] =
|
23
|
+
Config.spec_properties["vcenter"] ||
|
24
|
+
Config.cloud_options["properties"]["vcenters"].first.dup
|
25
|
+
|
26
|
+
properties["vcenter"]["address"] ||= properties["vcenter"]["host"]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Integer] size in MiB
|
31
|
+
def disk_size(cid)
|
32
|
+
disk_model.first(:uuid => cid).size
|
33
|
+
end
|
34
|
+
|
35
|
+
def persistent_disk_changed?
|
36
|
+
Config.resources["persistent_disk"] != disk_size(state.disk_cid)
|
37
|
+
end
|
38
|
+
|
39
|
+
def check_dependencies
|
40
|
+
if Bosh::Common.which(%w[genisoimage mkisofs]).nil?
|
41
|
+
err("either of 'genisoimage' or 'mkisofs' commands must be present")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|