bosh-bootstrap 0.6.0 → 0.7.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.
- data/.gitignore +2 -1
- data/.travis.yml +5 -0
- data/ChangeLog.md +27 -0
- data/README.md +8 -2
- data/Rakefile +3 -1
- data/bosh-bootstrap.gemspec +9 -5
- data/lib/bosh/providers/aws.rb +121 -11
- data/lib/bosh/providers/openstack.rb +1 -1
- data/lib/bosh-bootstrap/cli.rb +260 -86
- data/lib/bosh-bootstrap/helpers/settings_setter.rb +21 -0
- data/lib/bosh-bootstrap/stages/stage_micro_bosh_deploy/bosh_micro_deploy +44 -5
- data/lib/bosh-bootstrap/stages/stage_micro_bosh_deploy/download_micro_bosh_stemcell +18 -70
- data/lib/bosh-bootstrap/stages/stage_micro_bosh_deploy.rb +2 -3
- data/lib/bosh-bootstrap/stages/stage_prepare_inception_vm/configure_git +28 -0
- data/lib/bosh-bootstrap/stages/stage_prepare_inception_vm/install_base_packages +6 -1
- data/lib/bosh-bootstrap/stages/stage_prepare_inception_vm/install_bosh +3 -29
- data/lib/bosh-bootstrap/stages/stage_prepare_inception_vm/install_useful_gems +4 -1
- data/lib/bosh-bootstrap/stages/stage_prepare_inception_vm.rb +12 -4
- data/lib/bosh-bootstrap/version.rb +1 -1
- data/lib/bosh-bootstrap.rb +4 -0
- data/spec/assets/micro_bosh_yml/micro_bosh.aws_ec2.yml +35 -0
- data/spec/assets/micro_bosh_yml/micro_bosh.aws_vpc.yml +37 -0
- data/spec/spec_helper.rb +19 -1
- data/spec/unit/aws_spec.rb +137 -0
- data/spec/unit/bosh/providers/aws_spec.rb +160 -0
- data/spec/unit/cli_spec.rb +3 -3
- data/spec/unit/cli_ssh_spec.rb +42 -0
- data/spec/unit/cli_upgrade_inception_spec.rb +31 -0
- data/spec/unit/settings_setter_spec.rb +29 -0
- metadata +69 -11
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/ChangeLog.md
CHANGED
@@ -1,5 +1,32 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## v0.7
|
4
|
+
|
5
|
+
Notable:
|
6
|
+
|
7
|
+
* For existing users: please run "deploy --upgrade-deps" as new inception package (runit) added; and jazor/yaml_command CLIs installed
|
8
|
+
* Forces microbosh stemcells 0.8.1 which work with public gems (latest public stemcell does not work with public gems)
|
9
|
+
|
10
|
+
Added:
|
11
|
+
|
12
|
+
* `mosh` command - connect to Inception VM on trains over flaky internet connections (use instead of `ssh` or `tmux` command) [thx @mrdavidlang]
|
13
|
+
* `upgrade-inception` command - to perform an upgrade of the Inception VM without triggering a re-deploy of microbosh.
|
14
|
+
|
15
|
+
Changes:
|
16
|
+
|
17
|
+
* Inception VM now installs rubygems 2.0.0 & bundler 1.3.0
|
18
|
+
* Better idempotence for re-deploying microbosh - will delete&deploy after a failure; will deploy after a deletion.
|
19
|
+
* Downloads ubuntu 10.04 ISO to speed up custom stemcell builds
|
20
|
+
* Using redcard to ensure ruby 1.9 only
|
21
|
+
* `manifest.yml` stores the name of the stemcell created from a custom stemcell build; no longer re-creates stemcell each time
|
22
|
+
* git color is enabled on inception VM
|
23
|
+
|
24
|
+
Work in progress:
|
25
|
+
|
26
|
+
* AWS VPC support was begin by the core BOSH team; though work has stopped sadly.
|
27
|
+
* Growing number of specs mostly using Fog.mock! mode; tests being run on travis
|
28
|
+
|
29
|
+
|
3
30
|
## v0.6
|
4
31
|
|
5
32
|
Highlights:
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ The Stark & Wayne Bosh Bootstrapper is the simplest way to get a Micro BOSH runn
|
|
7
7
|
Bootstrap a Micro BOSH universe from one CLI command. Also allows SSH access and the ability to delete created Micro BOSHes.
|
8
8
|
|
9
9
|
```
|
10
|
-
$ bosh-bootstrap deploy
|
10
|
+
$ bosh-bootstrap deploy
|
11
11
|
Creating inception VM...
|
12
12
|
Creating micro BOSH VM...
|
13
13
|
|
@@ -22,11 +22,17 @@ It is now very simple to bootstrap a micro BOSH from a single, local CLI. The bo
|
|
22
22
|
|
23
23
|
To be cute about it, the Stark & Wayne Bosh Bootstrapper aims to provide lifecycle management for the BOSH lifecycle manager. Zing! See the "Deep dive into deploy command" section below for greater understanding why the Stark & Wayne Bosh Bootstrapper is very useful.
|
24
24
|
|
25
|
+
[](https://travis-ci.org/StarkAndWayne/bosh-bootstrap)
|
26
|
+
|
27
|
+
[](https://codeclimate.com/github/StarkAndWayne/bosh-bootstrap)
|
28
|
+
|
25
29
|
## Installation
|
26
30
|
|
27
|
-
This bootstrapper
|
31
|
+
This bootstrapper tool is distributed as a RubyGem for Ruby 1.9+.
|
28
32
|
|
29
33
|
```
|
34
|
+
$ ruby -v
|
35
|
+
ruby 1.9.3p385 ...
|
30
36
|
$ gem install bosh-bootstrap
|
31
37
|
```
|
32
38
|
|
data/Rakefile
CHANGED
@@ -16,7 +16,7 @@ if defined?(RSpec)
|
|
16
16
|
desc "Run Unit Tests"
|
17
17
|
unit_rspec_task = RSpec::Core::RakeTask.new(:unit) do |t|
|
18
18
|
t.pattern = "spec/unit/**/*_spec.rb"
|
19
|
-
t.rspec_opts = %w(--format progress --color
|
19
|
+
t.rspec_opts = %w(--format progress --color)
|
20
20
|
end
|
21
21
|
|
22
22
|
desc "Run Integration Tests"
|
@@ -29,3 +29,5 @@ if defined?(RSpec)
|
|
29
29
|
desc "Install dependencies and run tests"
|
30
30
|
task :spec => %w(spec:unit spec:functional)
|
31
31
|
end
|
32
|
+
|
33
|
+
task :default => :spec
|
data/bosh-bootstrap.gemspec
CHANGED
@@ -10,26 +10,30 @@ Gem::Specification.new do |gem|
|
|
10
10
|
gem.email = ["drnicwilliams@gmail.com"]
|
11
11
|
gem.description = %q{Bootstrap a micro BOSH universe from one CLI}
|
12
12
|
gem.summary = <<-EOS
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
an available stemcell.
|
13
|
+
bosh-bootstrap is a command line tool that you can run on your laptop and
|
14
|
+
automatically get a microbosh (and an inception VM) deployed on either
|
15
|
+
AWS or OpenStack.
|
17
16
|
EOS
|
18
17
|
gem.homepage = "https://github.com/StarkAndWayne/bosh-bootstrap"
|
19
18
|
|
19
|
+
gem.required_ruby_version = '>= 1.9'
|
20
|
+
|
20
21
|
gem.files = `git ls-files`.split($/)
|
21
22
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
22
23
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
23
24
|
gem.require_paths = ["lib"]
|
24
|
-
|
25
|
+
|
25
26
|
gem.add_dependency "thor"
|
26
27
|
gem.add_dependency "highline"
|
27
28
|
gem.add_dependency "settingslogic"
|
28
29
|
gem.add_dependency "POpen4"
|
30
|
+
gem.add_dependency "net-ssh", "~> 2.2.2"
|
29
31
|
gem.add_dependency "net-scp"
|
30
32
|
gem.add_dependency "fog", "~>1.8.0"
|
31
33
|
gem.add_dependency "escape"
|
34
|
+
gem.add_dependency "redcard"
|
32
35
|
gem.add_dependency "bosh_cli"
|
33
36
|
gem.add_development_dependency "rake"
|
34
37
|
gem.add_development_dependency "rspec"
|
38
|
+
gem.add_development_dependency "activesupport", ">= 3.0.0"
|
35
39
|
end
|
data/lib/bosh/providers/aws.rb
CHANGED
@@ -24,7 +24,7 @@ class Bosh::Providers::AWS < Bosh::Providers::BaseProvider
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
# @return [Hash] e.g. { :bits => 0, :cores => 2, :disk => 0,
|
27
|
+
# @return [Hash] e.g. { :bits => 0, :cores => 2, :disk => 0,
|
28
28
|
# :id => 't1.micro', :name => 'Micro Instance', :ram => 613}
|
29
29
|
# or nil if +server_flavor_id+ is not a supported flavor ID
|
30
30
|
def fog_compute_flavor(server_flavor_id)
|
@@ -32,7 +32,7 @@ class Bosh::Providers::AWS < Bosh::Providers::BaseProvider
|
|
32
32
|
end
|
33
33
|
|
34
34
|
# @return [Array] of [Hash] for each supported compute flavor
|
35
|
-
# Example [Hash] { :bits => 0, :cores => 2, :disk => 0,
|
35
|
+
# Example [Hash] { :bits => 0, :cores => 2, :disk => 0,
|
36
36
|
# :id => 't1.micro', :name => 'Micro Instance', :ram => 613}
|
37
37
|
def aws_compute_flavors
|
38
38
|
Fog::Compute::AWS::FLAVORS
|
@@ -42,10 +42,18 @@ class Bosh::Providers::AWS < Bosh::Providers::BaseProvider
|
|
42
42
|
aws_compute_flavors.map { |fl| fl[:id] }
|
43
43
|
end
|
44
44
|
|
45
|
+
# Provision an EC2 or VPC elastic IP addess.
|
46
|
+
# * VPC - provision_public_ip_address(vpc: true)
|
47
|
+
# * EC2 - provision_public_ip_address
|
45
48
|
# @return [String] provisions a new public IP address in target region
|
46
49
|
# TODO nil if none available
|
47
|
-
def provision_public_ip_address
|
48
|
-
|
50
|
+
def provision_public_ip_address(options={})
|
51
|
+
if options.delete(:vpc)
|
52
|
+
options[:domain] = "vpc"
|
53
|
+
else
|
54
|
+
options[:domain] = options.delete(:domain) || "standard"
|
55
|
+
end
|
56
|
+
address = fog_compute.addresses.create(options)
|
49
57
|
address.public_ip
|
50
58
|
# TODO catch error and return nil
|
51
59
|
end
|
@@ -55,8 +63,25 @@ class Bosh::Providers::AWS < Bosh::Providers::BaseProvider
|
|
55
63
|
address.server = server
|
56
64
|
end
|
57
65
|
|
66
|
+
def create_vpc(name, cidr_block)
|
67
|
+
vpc = fog_compute.vpcs.create(name: name, cidr_block: cidr_block)
|
68
|
+
vpc.id
|
69
|
+
end
|
70
|
+
|
71
|
+
# Creates a VPC subnet
|
72
|
+
# @return [String] the subnet_id
|
73
|
+
def create_subnet(vpc_id, cidr_block)
|
74
|
+
subnet = fog_compute.subnets.create(vpc_id: vpc_id, cidr_block: cidr_block)
|
75
|
+
subnet.subnet_id
|
76
|
+
end
|
77
|
+
|
78
|
+
def create_internet_gateway(vpc_id)
|
79
|
+
gateway = fog_compute.internet_gateways.create(vpc_id: vpc_id)
|
80
|
+
gateway.id
|
81
|
+
end
|
82
|
+
|
58
83
|
# Creates or reuses an AWS security group and opens ports.
|
59
|
-
#
|
84
|
+
#
|
60
85
|
# +security_group_name+ is the name to be created or reused
|
61
86
|
# +ports+ is a hash of name/port for ports to open, for example:
|
62
87
|
# {
|
@@ -64,6 +89,19 @@ class Bosh::Providers::AWS < Bosh::Providers::BaseProvider
|
|
64
89
|
# http: 80,
|
65
90
|
# https: 443
|
66
91
|
# }
|
92
|
+
# protocol defaults to TCP
|
93
|
+
# You can also use a more verbose +ports+ using the format:
|
94
|
+
# {
|
95
|
+
# ssh: 22,
|
96
|
+
# http: { ports: (80..82) },
|
97
|
+
# mosh: { protocol: "udp", ports: (60000..60050) }
|
98
|
+
# mosh: { protocol: "rdp", ports: (3398..3398), ip_ranges: [ { cidrIp: "196.212.12.34/32" } ] }
|
99
|
+
# }
|
100
|
+
# In this example,
|
101
|
+
# * TCP 22 will be opened for ssh from any ip_range,
|
102
|
+
# * TCP ports 80, 81, 82 for http from any ip_range,
|
103
|
+
# * UDP 60000 -> 60050 for mosh from any ip_range and
|
104
|
+
# * TCP 3398 for RDP from ip range: 96.212.12.34/32
|
67
105
|
def create_security_group(security_group_name, description, ports)
|
68
106
|
unless sg = fog_compute.security_groups.get(security_group_name)
|
69
107
|
sg = fog_compute.security_groups.create(name: security_group_name, description: description)
|
@@ -73,10 +111,11 @@ class Bosh::Providers::AWS < Bosh::Providers::BaseProvider
|
|
73
111
|
end
|
74
112
|
ip_permissions = sg.ip_permissions
|
75
113
|
ports_opened = 0
|
76
|
-
ports.each do |name,
|
77
|
-
|
78
|
-
|
79
|
-
|
114
|
+
ports.each do |name, port_defn|
|
115
|
+
(protocol, port_range, ip_range) = extract_port_definition(port_defn)
|
116
|
+
unless port_open?(ip_permissions, port_range, protocol, ip_range)
|
117
|
+
sg.authorize_port_range(port_range, {:ip_protocol => protocol, :cidr_ip => ip_range})
|
118
|
+
puts " -> opened #{name} ports #{protocol.upcase} #{port_range.min}..#{port_range.max} from IP range #{ip_range}"
|
80
119
|
ports_opened += 1
|
81
120
|
end
|
82
121
|
end
|
@@ -84,8 +123,40 @@ class Bosh::Providers::AWS < Bosh::Providers::BaseProvider
|
|
84
123
|
true
|
85
124
|
end
|
86
125
|
|
87
|
-
|
88
|
-
|
126
|
+
# Any of the following +port_defn+ can be used:
|
127
|
+
# {
|
128
|
+
# ssh: 22,
|
129
|
+
# http: { ports: (80..82) },
|
130
|
+
# mosh: { protocol: "udp", ports: (60000..60050) }
|
131
|
+
# mosh: { protocol: "rdp", ports: (3398..3398), ip_range: "196.212.12.34/32" }
|
132
|
+
# }
|
133
|
+
# In this example,
|
134
|
+
# * TCP 22 will be opened for ssh from any ip_range,
|
135
|
+
# * TCP ports 80, 81, 82 for http from any ip_range,
|
136
|
+
# * UDP 60000 -> 60050 for mosh from any ip_range and
|
137
|
+
# * TCP 3398 for RDP from ip range: 96.212.12.34/32
|
138
|
+
def extract_port_definition(port_defn)
|
139
|
+
protocol = "tcp"
|
140
|
+
ip_range = "0.0.0.0/0"
|
141
|
+
if port_defn.is_a? Integer
|
142
|
+
port_range = (port_defn..port_defn)
|
143
|
+
elsif port_defn.is_a? Range
|
144
|
+
port_range = port_defn
|
145
|
+
elsif port_defn.is_a? Hash
|
146
|
+
protocol = port_defn[:protocol] if port_defn[:protocol]
|
147
|
+
port_range = port_defn[:ports] if port_defn[:ports]
|
148
|
+
ip_range = port_defn[:ip_range] if port_defn[:ip_range]
|
149
|
+
end
|
150
|
+
[protocol, port_range, ip_range]
|
151
|
+
end
|
152
|
+
|
153
|
+
def port_open?(ip_permissions, port_range, protocol, ip_range)
|
154
|
+
ip_permissions && ip_permissions.find do |ip|
|
155
|
+
ip["ipProtocol"] == protocol \
|
156
|
+
&& ip["ipRanges"].detect { |range| range["cidrIp"] == ip_range } \
|
157
|
+
&& ip["fromPort"] <= port_range.min \
|
158
|
+
&& ip["toPort"] >= port_range.max
|
159
|
+
end
|
89
160
|
end
|
90
161
|
|
91
162
|
def find_server_device(server, device)
|
@@ -109,4 +180,43 @@ class Bosh::Providers::AWS < Bosh::Providers::BaseProvider
|
|
109
180
|
# Instead, using:
|
110
181
|
volume.server = server
|
111
182
|
end
|
183
|
+
|
184
|
+
def bootstrap(new_attributes = {})
|
185
|
+
vpc = new_attributes[:subnet_id]
|
186
|
+
|
187
|
+
server = fog_compute.servers.new(new_attributes)
|
188
|
+
|
189
|
+
unless new_attributes[:key_name]
|
190
|
+
# first or create fog_#{credential} keypair
|
191
|
+
name = Fog.respond_to?(:credential) && Fog.credential || :default
|
192
|
+
unless server.key_pair = fog_compute.key_pairs.get("fog_#{name}")
|
193
|
+
server.key_pair = fog_compute.key_pairs.create(
|
194
|
+
:name => "fog_#{name}",
|
195
|
+
:public_key => server.public_key
|
196
|
+
)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
if vpc
|
201
|
+
# TODO setup security group on new server
|
202
|
+
else
|
203
|
+
# make sure port 22 is open in the first security group
|
204
|
+
security_group = fog_compute.security_groups.get(server.groups.first)
|
205
|
+
authorized = security_group.ip_permissions.detect do |ip_permission|
|
206
|
+
ip_permission['ipRanges'].first && ip_permission['ipRanges'].first['cidrIp'] == '0.0.0.0/0' &&
|
207
|
+
ip_permission['fromPort'] == 22 &&
|
208
|
+
ip_permission['ipProtocol'] == 'tcp' &&
|
209
|
+
ip_permission['toPort'] == 22
|
210
|
+
end
|
211
|
+
unless authorized
|
212
|
+
security_group.authorize_port_range(22..22)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
server.save
|
217
|
+
server.wait_for { ready? }
|
218
|
+
server.setup(:key_data => [server.private_key])
|
219
|
+
server
|
220
|
+
end
|
221
|
+
|
112
222
|
end
|
@@ -7,7 +7,7 @@ require "bosh/providers/base_provider"
|
|
7
7
|
class Bosh::Providers::OpenStack < Bosh::Providers::BaseProvider
|
8
8
|
# @return [String] provisions a new public IP address in target region
|
9
9
|
# TODO nil if none available
|
10
|
-
def provision_public_ip_address
|
10
|
+
def provision_public_ip_address(options={})
|
11
11
|
address = fog_compute.addresses.create
|
12
12
|
address.ip
|
13
13
|
# TODO catch error and return nil
|
data/lib/bosh-bootstrap/cli.rb
CHANGED
@@ -15,6 +15,7 @@ module Bosh::Bootstrap
|
|
15
15
|
include Thor::Actions
|
16
16
|
include Bosh::Bootstrap::Helpers::FogSetup
|
17
17
|
include Bosh::Bootstrap::Helpers::Settings
|
18
|
+
include Bosh::Bootstrap::Helpers::SettingsSetter
|
18
19
|
include FileUtils
|
19
20
|
|
20
21
|
attr_reader :fog_credentials
|
@@ -39,11 +40,20 @@ module Bosh::Bootstrap
|
|
39
40
|
deploy_stage_6_setup_new_bosh
|
40
41
|
end
|
41
42
|
|
43
|
+
desc "upgrade-inception", "Upgrade inception VM with latest packages, gems, security group ports"
|
44
|
+
method_option :"edge-deployer", :type => :boolean, :desc => "Install bosh deployer from git instead of rubygems"
|
45
|
+
def upgrade_inception
|
46
|
+
load_deploy_options # from method_options above
|
47
|
+
|
48
|
+
setup_server
|
49
|
+
upgrade_inception_stage_1_prepare_inception_vm
|
50
|
+
end
|
51
|
+
|
42
52
|
# desc "delete", "Delete Micro BOSH"
|
43
53
|
# method_option :all, :type => :boolean, :desc => "Delete all micro-boshes and inception VM [coming soon]"
|
44
54
|
# def delete
|
45
55
|
# delete_stage_1_target_inception_vm
|
46
|
-
#
|
56
|
+
#
|
47
57
|
# if options[:all]
|
48
58
|
# error "I'm sorry; the awesome --all flag is not yet implemented"
|
49
59
|
# delete_all_stage_2_delete_micro_boshes
|
@@ -52,7 +62,7 @@ module Bosh::Bootstrap
|
|
52
62
|
# delete_one_stage_2_delete_micro_bosh
|
53
63
|
# end
|
54
64
|
# end
|
55
|
-
#
|
65
|
+
#
|
56
66
|
desc "ssh [COMMAND]", "Open an ssh session to the inception VM [do nothing if local machine is inception VM]"
|
57
67
|
long_desc <<-DESC
|
58
68
|
If a command is supplied, it will be run, otherwise a session will be
|
@@ -64,13 +74,22 @@ module Bosh::Bootstrap
|
|
64
74
|
|
65
75
|
desc "tmux", "Open an ssh (with tmux) session to the inception VM [do nothing if local machine is inception VM]"
|
66
76
|
long_desc <<-DESC
|
67
|
-
Opens a connection using ssh and attaches to the most recent tmux session;
|
77
|
+
Opens a connection using ssh and attaches to the most recent tmux session;
|
68
78
|
giving you persistance across disconnects.
|
69
79
|
DESC
|
70
80
|
def tmux
|
71
81
|
run_ssh_command_or_open_tunnel(["-t", "tmux attach || tmux new-session"])
|
72
82
|
end
|
73
83
|
|
84
|
+
desc "mosh", "Open an mosh session to the inception VM [do nothing if local machine is inception VM]"
|
85
|
+
long_desc <<-DESC
|
86
|
+
Opens a connection using MOSH (http://mosh.mit.edu/); ideal for those with slow or flakey internet connections.
|
87
|
+
Requires outgoing UDP port 60001 to the Inception VM
|
88
|
+
DESC
|
89
|
+
def mosh
|
90
|
+
open_mosh_session
|
91
|
+
end
|
92
|
+
|
74
93
|
no_tasks do
|
75
94
|
DEFAULT_INCEPTION_VOLUME_SIZE = 32 # Gb
|
76
95
|
|
@@ -79,12 +98,25 @@ module Bosh::Bootstrap
|
|
79
98
|
unless settings[:fog_credentials]
|
80
99
|
choose_fog_provider
|
81
100
|
end
|
101
|
+
|
102
|
+
unless settings[:bosh_cloud_properties]
|
103
|
+
build_cloud_properties
|
104
|
+
end
|
82
105
|
confirm "Using infrastructure provider #{settings.fog_credentials.provider}"
|
83
106
|
|
107
|
+
if aws?
|
108
|
+
if ENV['VPC']
|
109
|
+
choose_aws_vpc_or_ec2
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
84
113
|
unless settings[:region_code]
|
85
114
|
choose_provider_region
|
86
115
|
end
|
87
|
-
if settings[:region_code]
|
116
|
+
if region = settings[:region_code]
|
117
|
+
settings["fog_credentials"]["region"] = region
|
118
|
+
settings["bosh_cloud_properties"][settings["bosh_provider"]]["region"] = region
|
119
|
+
settings["bosh_cloud_properties"][settings["bosh_provider"]]["ec2_endpoint"] = "ec2.#{region}.amazonaws.com"
|
88
120
|
confirm "Using #{settings.fog_credentials.provider} region #{settings.region_code}"
|
89
121
|
else
|
90
122
|
confirm "No specific region/data center for #{settings.fog_credentials.provider}"
|
@@ -97,7 +129,7 @@ module Bosh::Bootstrap
|
|
97
129
|
confirm "Using #{settings.fog_credentials.provider} network labelled #{settings['network_label']}"
|
98
130
|
end
|
99
131
|
end
|
100
|
-
|
132
|
+
|
101
133
|
def deploy_stage_2_bosh_configuration
|
102
134
|
header "Stage 2: BOSH configuration"
|
103
135
|
unless settings[:bosh_name]
|
@@ -134,9 +166,13 @@ module Bosh::Bootstrap
|
|
134
166
|
end
|
135
167
|
|
136
168
|
unless settings[:bosh]["ip_address"]
|
137
|
-
|
138
|
-
|
139
|
-
|
169
|
+
if vpc?
|
170
|
+
settings[:bosh]["ip_address"] = "10.0.0.6"
|
171
|
+
else
|
172
|
+
say "Acquiring IP address for micro BOSH..."
|
173
|
+
ip_address = acquire_ip_address
|
174
|
+
settings[:bosh]["ip_address"] = ip_address
|
175
|
+
end
|
140
176
|
end
|
141
177
|
unless settings[:bosh]["ip_address"]
|
142
178
|
error "IP address not available/provided currently"
|
@@ -145,6 +181,10 @@ module Bosh::Bootstrap
|
|
145
181
|
end
|
146
182
|
save_settings!
|
147
183
|
|
184
|
+
if aws? && vpc?
|
185
|
+
create_complete_vpc(settings.bosh_name, "10.0.0.0/16", "10.0.0.0/24")
|
186
|
+
end
|
187
|
+
|
148
188
|
unless settings[:bosh_security_group]
|
149
189
|
security_group_name = settings.bosh_name
|
150
190
|
create_security_group(security_group_name)
|
@@ -169,12 +209,12 @@ module Bosh::Bootstrap
|
|
169
209
|
|
170
210
|
def deploy_stage_3_create_allocate_inception_vm
|
171
211
|
header "Stage 3: Create/Allocate the Inception VM"
|
172
|
-
unless settings["inception"]
|
212
|
+
unless settings["inception"]
|
173
213
|
hl.choose do |menu|
|
174
214
|
menu.prompt = "Create or specify an Inception VM: "
|
175
215
|
if aws? || openstack?
|
176
216
|
menu.choice("create new inception VM") do
|
177
|
-
|
217
|
+
settings["inception"] = {"create_new" => true}
|
178
218
|
end
|
179
219
|
end
|
180
220
|
menu.choice("use an existing Ubuntu server") do
|
@@ -191,18 +231,18 @@ module Bosh::Bootstrap
|
|
191
231
|
end
|
192
232
|
end
|
193
233
|
end
|
194
|
-
# If successfully validate inception VM, then save those settings.
|
195
234
|
save_settings!
|
196
235
|
|
197
|
-
if settings["inception"]["host"]
|
198
|
-
|
199
|
-
confirm "Using inception VM #{settings.inception.username}@#{settings.inception.host}"
|
200
|
-
else
|
201
|
-
@server = Commander::LocalServer.new
|
202
|
-
confirm "Using this server as the inception VM"
|
236
|
+
if settings["inception"]["create_new"] && !settings["inception"]["host"]
|
237
|
+
aws? ? boot_aws_inception_vm : boot_openstack_inception_vm
|
203
238
|
end
|
239
|
+
# If successfully validate inception VM, then save those settings.
|
240
|
+
save_settings!
|
241
|
+
|
242
|
+
setup_server
|
243
|
+
|
204
244
|
unless settings["inception"]["validated"]
|
205
|
-
unless
|
245
|
+
unless run_server(Bosh::Bootstrap::Stages::StageValidateInceptionVm.new(settings).commands)
|
206
246
|
error "Failed to complete Stage 3: Create/Allocate the Inception VM"
|
207
247
|
end
|
208
248
|
settings["inception"]["validated"] = true
|
@@ -255,42 +295,72 @@ module Bosh::Bootstrap
|
|
255
295
|
confirm "You are now targeting and logged in to your BOSH"
|
256
296
|
end
|
257
297
|
|
258
|
-
def
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
298
|
+
def upgrade_inception_stage_1_prepare_inception_vm
|
299
|
+
if settings["inception"] && settings["inception"]["prepared"]
|
300
|
+
header "Stage 1: Upgrade Inception VM"
|
301
|
+
unless run_server(Bosh::Bootstrap::Stages::StagePrepareInceptionVm.new(settings).commands)
|
302
|
+
error "Failed to complete Stage 2: Upgrade Inception VM"
|
303
|
+
end
|
263
304
|
else
|
264
|
-
|
265
|
-
confirm "Using this server as the inception VM"
|
305
|
+
error "Please deploy an Inception VM first, using 'bosh-bootstrap deploy' command."
|
266
306
|
end
|
267
307
|
end
|
268
308
|
|
309
|
+
def delete_stage_1_target_inception_vm
|
310
|
+
header "Stage 1: Target inception VM to use to delete micro-bosh"
|
311
|
+
setup_server
|
312
|
+
end
|
313
|
+
|
269
314
|
def delete_one_stage_2_delete_micro_bosh
|
270
315
|
header "Stage 2: Deleting micro BOSH"
|
271
|
-
unless
|
316
|
+
unless run_server(Bosh::Bootstrap::Stages::MicroBoshDelete.new(settings).commands)
|
272
317
|
error "Failed to complete Stage 1: Delete micro BOSH"
|
273
318
|
end
|
274
319
|
save_settings!
|
275
320
|
end
|
276
321
|
|
277
322
|
def delete_all_stage_2_delete_micro_boshes
|
278
|
-
|
323
|
+
|
279
324
|
end
|
280
325
|
|
281
326
|
def delete_all_stage_3_delete_inception_vm
|
282
|
-
|
327
|
+
|
283
328
|
end
|
284
329
|
|
285
|
-
def
|
286
|
-
|
287
|
-
|
288
|
-
|
330
|
+
def setup_server
|
331
|
+
if settings["inception"]["host"]
|
332
|
+
@server = Commander::RemoteServer.new(settings.inception.host, settings.local.private_key_path)
|
333
|
+
confirm "Using inception VM #{settings.inception.username}@#{settings.inception.host}"
|
334
|
+
else
|
335
|
+
@server = Commander::LocalServer.new
|
336
|
+
confirm "Using this server as the inception VM"
|
289
337
|
end
|
290
|
-
|
291
|
-
|
338
|
+
end
|
339
|
+
|
340
|
+
def create_complete_vpc(name, vpc_range="10.0.0.0/16", subnet_cidr_block="10.0.0.0/24")
|
341
|
+
with_setting "vpc" do |setting|
|
342
|
+
say "Creating VPC '#{name}'..."
|
343
|
+
setting["id"] = provider.create_vpc(name, vpc_range)
|
344
|
+
end
|
345
|
+
|
346
|
+
vpc_id = settings["vpc"]["id"]
|
347
|
+
with_setting "internet_gateway" do |setting|
|
348
|
+
say "Creating internet gateway..."
|
349
|
+
setting["id"] = provider.create_internet_gateway(vpc_id)
|
292
350
|
end
|
351
|
+
|
352
|
+
with_setting "subnet" do |setting|
|
353
|
+
say "Creating subnet #{subnet_cidr_block}..."
|
354
|
+
setting["id"] = provider.create_subnet(vpc_id, subnet_cidr_block)
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
def run_ssh_command_or_open_tunnel(cmd)
|
359
|
+
ensure_inception_vm
|
360
|
+
ensure_inception_vm_has_launched
|
361
|
+
|
293
362
|
username = 'vcap'
|
363
|
+
host = settings.inception[:host]
|
294
364
|
result = system Escape.shell_command(['ssh', "#{username}@#{host}", cmd].flatten.compact)
|
295
365
|
exit result
|
296
366
|
|
@@ -302,6 +372,59 @@ module Bosh::Bootstrap
|
|
302
372
|
# Warning: Identity file /Users/drnic/.ssh/id_rsa not accessible: No such file or directory.
|
303
373
|
end
|
304
374
|
|
375
|
+
def ensure_inception_vm
|
376
|
+
unless settings[:inception]
|
377
|
+
say "No inception VM being used", :yellow
|
378
|
+
exit 0
|
379
|
+
end
|
380
|
+
end
|
381
|
+
def ensure_inception_vm_has_launched
|
382
|
+
unless settings.inception[:host]
|
383
|
+
exit "Inception VM has not finished launching; run to complete: #{self.class.banner_base} deploy"
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
def open_mosh_session
|
388
|
+
ensure_mosh_installed
|
389
|
+
ensure_inception_vm
|
390
|
+
ensure_inception_vm_has_launched
|
391
|
+
ensure_security_group_allows_mosh
|
392
|
+
|
393
|
+
username = 'vcap'
|
394
|
+
host = settings.inception[:host]
|
395
|
+
exit system Escape.shell_command(['mosh', "#{username}@#{host}"])
|
396
|
+
end
|
397
|
+
|
398
|
+
def ensure_mosh_installed
|
399
|
+
system 'mosh --version'
|
400
|
+
unless $?.exitstatus == 255 #mosh --version returns exit code 255, rather than 0 as one might expect. Grrr.
|
401
|
+
say "You must have MOSH installed to use this command. See http://mosh.mit.edu/#getting", :yellow
|
402
|
+
exit 0
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def ensure_security_group_allows_mosh
|
407
|
+
ports = {
|
408
|
+
mosh: {
|
409
|
+
protocol: "udp",
|
410
|
+
ports: (60000..60050)
|
411
|
+
}
|
412
|
+
}
|
413
|
+
inception_server = fog_compute.servers.get(settings["inception"]["server_id"])
|
414
|
+
security_group_name = inception_server.groups.first
|
415
|
+
|
416
|
+
say "Ensuring #{ports[:mosh][:protocol]} ports #{ports[:mosh][:ports].to_s} are open", [:yellow, :bold]
|
417
|
+
say "on Inception VM's security group (#{security_group_name}) ...", [:yellow, :bold]
|
418
|
+
|
419
|
+
#TODO - remove this guard once the other providers have been extended
|
420
|
+
unless settings['bosh_provider'] == 'aws'
|
421
|
+
say "TODO: Non-AWS providers need to be extended to allow creation of UDP ports (60000..60050) in their security groups", :yellow
|
422
|
+
exit 0
|
423
|
+
end
|
424
|
+
|
425
|
+
provider.create_security_group(security_group_name, 'not used', ports)
|
426
|
+
end
|
427
|
+
|
305
428
|
# Display header for a new section of the bootstrapper
|
306
429
|
def header(title, options={})
|
307
430
|
say "" # golden whitespace
|
@@ -327,6 +450,10 @@ module Bosh::Bootstrap
|
|
327
450
|
def load_deploy_options
|
328
451
|
settings["fog_path"] = File.expand_path(options[:fog] || "~/.fog")
|
329
452
|
|
453
|
+
settings["git"] ||= {}
|
454
|
+
settings["git"]["name"] ||= `git config user.name`.strip
|
455
|
+
settings["git"]["email"] ||= `git config user.email`.strip
|
456
|
+
|
330
457
|
settings["bosh_git_source"] = options[:"edge-deployer"] # use bosh git repo instead of rubygems
|
331
458
|
|
332
459
|
# determine which micro-bosh stemcell to download/create
|
@@ -459,6 +586,10 @@ module Bosh::Bootstrap
|
|
459
586
|
@fog_credentials.each do |key, value|
|
460
587
|
settings[:fog_credentials][key] = value
|
461
588
|
end
|
589
|
+
save_settings!
|
590
|
+
end
|
591
|
+
|
592
|
+
def build_cloud_properties
|
462
593
|
setup_bosh_cloud_properties
|
463
594
|
settings[:bosh_provider] = settings.bosh_cloud_properties.keys.first # aws, vsphere...
|
464
595
|
save_settings!
|
@@ -506,7 +637,7 @@ module Bosh::Bootstrap
|
|
506
637
|
props[:access_key_id] = settings.fog_credentials.aws_access_key_id
|
507
638
|
props[:secret_access_key] = settings.fog_credentials.aws_secret_access_key
|
508
639
|
# props[:ec2_endpoint] = "ec2.REGION.amazonaws.com" - via +choose_aws_region+
|
509
|
-
# props[:region] = REGION - via +choose_aws_region+
|
640
|
+
# props[:region] = REGION - via +choose_aws_region+
|
510
641
|
# props[:default_key_name] = "microbosh" - via +create_aws_key_pair+
|
511
642
|
# props[:ec2_private_key] = "/home/vcap/.ssh/microbosh.pem" - via +create_aws_key_pair+
|
512
643
|
# props[:default_security_groups] = ["microbosh"], - via +create_aws_security_group+
|
@@ -561,6 +692,16 @@ module Bosh::Bootstrap
|
|
561
692
|
end
|
562
693
|
end
|
563
694
|
|
695
|
+
def choose_aws_vpc_or_ec2
|
696
|
+
if settings["use_vpc"].nil?
|
697
|
+
settings["use_vpc"] = begin
|
698
|
+
answer = hl.ask("You want to use VPC, right? ") {|q| q.default="yes"; q.validate = /(yes|no)/i }.match(/y/)
|
699
|
+
!!answer
|
700
|
+
end
|
701
|
+
save_settings!
|
702
|
+
end
|
703
|
+
end
|
704
|
+
|
564
705
|
def choose_aws_region
|
565
706
|
aws_regions = provider.region_labels
|
566
707
|
default_aws_region = provider.default_region_label
|
@@ -570,9 +711,6 @@ module Bosh::Bootstrap
|
|
570
711
|
aws_regions.each do |region|
|
571
712
|
menu.choice(region) do
|
572
713
|
settings["region_code"] = region
|
573
|
-
settings["fog_credentials"]["region"] = region
|
574
|
-
settings["bosh_cloud_properties"]["aws"]["region"] = region
|
575
|
-
settings["bosh_cloud_properties"]["aws"]["ec2_endpoint"] = "ec2.#{region}.amazonaws.com"
|
576
714
|
save_settings!
|
577
715
|
end
|
578
716
|
menu.default = default_aws_region
|
@@ -682,18 +820,32 @@ module Bosh::Bootstrap
|
|
682
820
|
def boot_aws_inception_vm
|
683
821
|
say "" # glowing whitespace
|
684
822
|
|
823
|
+
unless settings["inception"]["ip_address"]
|
824
|
+
say "Provisioning IP address for inception VM..."
|
825
|
+
settings["inception"]["ip_address"] = acquire_ip_address
|
826
|
+
save_settings!
|
827
|
+
end
|
828
|
+
|
685
829
|
public_key_path, private_key_path = local_ssh_key_paths
|
686
830
|
unless settings["inception"] && settings["inception"]["server_id"]
|
687
831
|
username = "ubuntu"
|
688
832
|
size = "m1.small"
|
833
|
+
ip_address = settings["inception"]["ip_address"]
|
689
834
|
say "Provisioning #{size} for inception VM..."
|
690
|
-
|
835
|
+
inception_vm_attributes = {
|
691
836
|
:public_key_path => public_key_path,
|
692
837
|
:private_key_path => private_key_path,
|
693
838
|
:flavor_id => size,
|
694
839
|
:bits => 64,
|
695
|
-
:username => "ubuntu"
|
696
|
-
|
840
|
+
:username => "ubuntu",
|
841
|
+
:public_ip_address => ip_address
|
842
|
+
}
|
843
|
+
if vpc?
|
844
|
+
raise "must create subnet before creating VPC inception VM" unless settings["subnet"] && settings["subnet"]["id"]
|
845
|
+
inception_vm_attributes[:subnet_id] = settings["subnet"]["id"]
|
846
|
+
inception_vm_attributes[:private_ip_address] = "10.0.0.5"
|
847
|
+
end
|
848
|
+
server = provider.bootstrap(inception_vm_attributes)
|
697
849
|
unless server
|
698
850
|
error "Something mysteriously cloudy happened and fog could not provision a VM. Please check your limits."
|
699
851
|
end
|
@@ -706,16 +858,6 @@ module Bosh::Bootstrap
|
|
706
858
|
|
707
859
|
server ||= fog_compute.servers.get(settings["inception"]["server_id"])
|
708
860
|
|
709
|
-
unless settings["inception"]["ip_address"]
|
710
|
-
say "Provisioning IP address for inception VM..."
|
711
|
-
ip_address = acquire_ip_address
|
712
|
-
associate_ip_address_with_server(ip_address, server)
|
713
|
-
host = server.dns_name
|
714
|
-
|
715
|
-
settings["inception"]["ip_address"] = ip_address
|
716
|
-
save_settings!
|
717
|
-
end
|
718
|
-
|
719
861
|
unless settings["inception"]["disk_size"]
|
720
862
|
disk_size = DEFAULT_INCEPTION_VOLUME_SIZE # Gb
|
721
863
|
device = "/dev/sdi"
|
@@ -884,7 +1026,7 @@ module Bosh::Bootstrap
|
|
884
1026
|
# For AWS, it will dynamically provision an elastic IP
|
885
1027
|
# For OpenStack, it will dynamically provision a floating IP
|
886
1028
|
def acquire_ip_address
|
887
|
-
unless public_ip = provider.provision_public_ip_address
|
1029
|
+
unless public_ip = provider.provision_public_ip_address(vpc: vpc?)
|
888
1030
|
say "Unable to acquire a public IP. Please check your account for capacity or service issues.".red
|
889
1031
|
exit 1
|
890
1032
|
end
|
@@ -908,14 +1050,26 @@ module Bosh::Bootstrap
|
|
908
1050
|
|
909
1051
|
# Format and mount the volume
|
910
1052
|
say "Mounting persistent disk as volume on inception VM..."
|
911
|
-
|
912
|
-
|
1053
|
+
run_ssh_command_until_successful server, "sudo mkfs.ext4 #{device} -F"
|
1054
|
+
run_ssh_command_until_successful server, "sudo mkdir -p /var/vcap/store"
|
1055
|
+
run_ssh_command_until_successful server, "sudo mount #{device} /var/vcap/store"
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
def run_ssh_command_until_successful(server, cmd)
|
1059
|
+
completed = false
|
1060
|
+
until completed
|
913
1061
|
begin
|
914
|
-
|
915
|
-
server.ssh([
|
916
|
-
|
917
|
-
|
918
|
-
|
1062
|
+
say "Running on inception VM: #{cmd}"
|
1063
|
+
result = server.ssh([cmd]).first
|
1064
|
+
if result.status == 1
|
1065
|
+
result.display_stdout
|
1066
|
+
result.display_stderr
|
1067
|
+
sleep 1
|
1068
|
+
say "trying again..."
|
1069
|
+
next
|
1070
|
+
else
|
1071
|
+
end
|
1072
|
+
completed = true
|
919
1073
|
rescue Errno::ETIMEDOUT => e
|
920
1074
|
say "Timeout error/warning mounting volume, retrying...", yellow
|
921
1075
|
end
|
@@ -953,6 +1107,10 @@ module Bosh::Bootstrap
|
|
953
1107
|
settings.fog_credentials.provider == "AWS"
|
954
1108
|
end
|
955
1109
|
|
1110
|
+
def vpc?
|
1111
|
+
settings["use_vpc"]
|
1112
|
+
end
|
1113
|
+
|
956
1114
|
def openstack?
|
957
1115
|
settings.fog_credentials.provider == "OpenStack"
|
958
1116
|
end
|
@@ -967,35 +1125,45 @@ module Bosh::Bootstrap
|
|
967
1125
|
|
968
1126
|
# Returns the latest micro-bosh stemcell
|
969
1127
|
# for the target provider (aws, vsphere, openstack)
|
1128
|
+
# The name includes the version number.
|
970
1129
|
def micro_bosh_stemcell_name
|
971
|
-
@micro_bosh_stemcell_name ||=
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
1130
|
+
@micro_bosh_stemcell_name ||= "micro-bosh-stemcell-#{provider_name}-#{known_stable_micro_bosh_stemcell_version}.tgz"
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
def known_stable_micro_bosh_stemcell_version
|
1134
|
+
"0.8.1"
|
1135
|
+
end
|
1136
|
+
|
1137
|
+
def latest_micro_bosh_stemcell_name
|
1138
|
+
stemcell_filter_tags = ['micro', provider_name]
|
1139
|
+
if settings["micro_bosh_stemcell_type"] == "stable"
|
1140
|
+
unless openstack?
|
1141
|
+
# FIXME remove this if when openstack has its first stable
|
1142
|
+
stemcell_filter_tags << "stable" # latest stable micro-bosh stemcell by default
|
978
1143
|
end
|
979
|
-
tags = stemcell_filter_tags.join(",")
|
980
|
-
bosh_stemcells_cmd = "bosh public stemcells --tags #{tags}"
|
981
|
-
say "Locating micro-bosh stemcell, running '#{bosh_stemcells_cmd}'..."
|
982
|
-
#
|
983
|
-
# The +bosh_stemcells_cmd+ has an output that looks like:
|
984
|
-
# +-----------------------------------+--------------------+
|
985
|
-
# | Name | Tags |
|
986
|
-
# +-----------------------------------+--------------------+
|
987
|
-
# | micro-bosh-stemcell-aws-0.6.4.tgz | aws, micro, stable |
|
988
|
-
# | micro-bosh-stemcell-aws-0.7.0.tgz | aws, micro, test |
|
989
|
-
# +-----------------------------------+--------------------+
|
990
|
-
#
|
991
|
-
# So to get the latest version for the filter tags,
|
992
|
-
# get the Name field, reverse sort, and return the first item
|
993
|
-
# Effectively:
|
994
|
-
# `#{bosh_stemcells_cmd} | grep micro | awk '{ print $2 }' | sort -r | head -n 1`.strip
|
995
|
-
stemcell_output = `#{bosh_stemcells_cmd}`
|
996
|
-
say stemcell_output
|
997
|
-
stemcell_output.scan(/[\w.-]+\.tgz/).last
|
998
1144
|
end
|
1145
|
+
tags = stemcell_filter_tags.join(",")
|
1146
|
+
bosh_stemcells_cmd = "bosh public stemcells --tags #{tags}"
|
1147
|
+
say "Locating micro-bosh stemcell, running '#{bosh_stemcells_cmd}'..."
|
1148
|
+
#
|
1149
|
+
# The +bosh_stemcells_cmd+ has an output that looks like:
|
1150
|
+
# +----------------------------------------+--------------------+
|
1151
|
+
# | Name | Tags |
|
1152
|
+
# +----------------------------------------+--------------------+
|
1153
|
+
# | micro-bosh-stemcell-aws-0.6.4.tgz | aws, micro, stable |
|
1154
|
+
# | micro-bosh-stemcell-aws-0.7.0.tgz | aws, micro, test |
|
1155
|
+
# | micro-bosh-stemcell-aws-0.8.1.tgz | aws, micro, test |
|
1156
|
+
# | micro-bosh-stemcell-aws-1.5.0.pre1.tgz | aws, micro |
|
1157
|
+
# | micro-bosh-stemcell-aws-1.5.0.pre2.tgz | aws, micro |
|
1158
|
+
# +----------------------------------------+--------------------+
|
1159
|
+
#
|
1160
|
+
# So to get the latest version for the filter tags,
|
1161
|
+
# get the Name field, reverse sort, and return the first item
|
1162
|
+
# Effectively:
|
1163
|
+
# `#{bosh_stemcells_cmd} | grep micro | awk '{ print $2 }' | sort -r | head -n 1`.strip
|
1164
|
+
stemcell_output = `#{bosh_stemcells_cmd}`
|
1165
|
+
say stemcell_output
|
1166
|
+
stemcell_output.scan(/[\w.-]+\.tgz/).last
|
999
1167
|
end
|
1000
1168
|
|
1001
1169
|
def provider_name
|
@@ -1007,6 +1175,12 @@ module Bosh::Bootstrap
|
|
1007
1175
|
@provider ||= Bosh::Providers.for_bosh_provider_name(settings.bosh_provider, fog_compute)
|
1008
1176
|
end
|
1009
1177
|
|
1178
|
+
# The micro_bosh.yml that is uploaded to the Inception VM before deploying the
|
1179
|
+
# MicroBOSH
|
1180
|
+
def micro_bosh_yml
|
1181
|
+
Bosh::Bootstrap::Stages::MicroBoshDeploy.new(settings).micro_bosh_manifest
|
1182
|
+
end
|
1183
|
+
|
1010
1184
|
def cyan; "\033[36m" end
|
1011
1185
|
def clear; "\033[0m" end
|
1012
1186
|
def bold; "\033[1m" end
|
@@ -1020,4 +1194,4 @@ module Bosh::Bootstrap
|
|
1020
1194
|
end
|
1021
1195
|
end
|
1022
1196
|
end
|
1023
|
-
end
|
1197
|
+
end
|