bosh-bootstrap 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/StarkAndWayne/bosh-bootstrap.png?branch=master)](https://travis-ci.org/StarkAndWayne/bosh-bootstrap)
|
26
|
+
|
27
|
+
[![Code Climate](https://codeclimate.com/github/StarkAndWayne/bosh-bootstrap.png)](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
|