inception 0.1.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/.chef/knife.rb +4 -0
- data/.gitignore +20 -0
- data/.kitchen.yml +42 -0
- data/.rspec +3 -0
- data/.travis.yml +23 -0
- data/Berksfile +8 -0
- data/Berksfile.lock +9 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +22 -0
- data/README.md +139 -0
- data/Rakefile +66 -0
- data/TODO.md +26 -0
- data/bin/bosh-inception +8 -0
- data/config/ssh/kitchen-aws +23 -0
- data/cookbooks/bosh_inception/README.md +15 -0
- data/cookbooks/bosh_inception/attributes/default.rb +20 -0
- data/cookbooks/bosh_inception/files/default/Gemfile.cf +4 -0
- data/cookbooks/bosh_inception/files/default/Gemfile.micro +5 -0
- data/cookbooks/bosh_inception/metadata.rb +32 -0
- data/cookbooks/bosh_inception/recipes/default.rb +15 -0
- data/cookbooks/bosh_inception/recipes/install_bosh.rb +37 -0
- data/cookbooks/bosh_inception/recipes/install_ruby.rb +10 -0
- data/cookbooks/bosh_inception/recipes/mount_store_volume.rb +24 -0
- data/cookbooks/bosh_inception/recipes/packages.rb +23 -0
- data/cookbooks/bosh_inception/recipes/setup_git.rb +34 -0
- data/cookbooks/bosh_inception/recipes/useful_dirs.rb +13 -0
- data/inception.gemspec +42 -0
- data/lib/bosh/providers.rb +41 -0
- data/lib/bosh/providers/README.md +5 -0
- data/lib/bosh/providers/cli/aws_provider_cli.rb +58 -0
- data/lib/bosh/providers/cli/openstack_provider_cli.rb +47 -0
- data/lib/bosh/providers/cli/provider_cli.rb +17 -0
- data/lib/bosh/providers/clients/aws_provider_client.rb +168 -0
- data/lib/bosh/providers/clients/fog_provider_client.rb +161 -0
- data/lib/bosh/providers/clients/openstack_provider_client.rb +65 -0
- data/lib/bosh/providers/constants/aws_constants.rb +25 -0
- data/lib/bosh/providers/constants/openstack_constants.rb +12 -0
- data/lib/inception.rb +9 -0
- data/lib/inception/cli.rb +136 -0
- data/lib/inception/cli_helpers/display.rb +26 -0
- data/lib/inception/cli_helpers/infrastructure.rb +157 -0
- data/lib/inception/cli_helpers/interactions.rb +15 -0
- data/lib/inception/cli_helpers/prepare_deploy_settings.rb +89 -0
- data/lib/inception/cli_helpers/provider.rb +14 -0
- data/lib/inception/cli_helpers/settings.rb +47 -0
- data/lib/inception/inception_server.rb +305 -0
- data/lib/inception/inception_server_cookbook.rb +89 -0
- data/lib/inception/next_deploy_actions.rb +20 -0
- data/lib/inception/version.rb +3 -0
- data/nodes/.gitkeep +0 -0
- data/spec/assets/.gitkeep +0 -0
- data/spec/assets/gitconfig +5 -0
- data/spec/assets/settings/aws-before-server.yml +14 -0
- data/spec/assets/settings/aws-created-server.yml +31 -0
- data/spec/integration/.gitkeep +0 -0
- data/spec/integration/aws/aws_basic_spec.rb +39 -0
- data/spec/spec_helper.rb +50 -0
- data/spec/support/aws/aws_helpers.rb +73 -0
- data/spec/support/settings_helper.rb +20 -0
- data/spec/support/stdout_capture.rb +17 -0
- data/spec/unit/.gitkeep +0 -0
- data/spec/unit/bosh/providers/aws_spec.rb +199 -0
- data/spec/unit/cli_delete_spec.rb +39 -0
- data/spec/unit/cli_deploy_aws_spec.rb +84 -0
- data/spec/unit/cli_ssh_spec.rb +82 -0
- data/spec/unit/inception_server_cookbook_spec.rb +61 -0
- data/spec/unit/inception_server_spec.rb +58 -0
- data/test/integration/default/bats/discover_user.bash +2 -0
- data/test/integration/default/bats/install_ruby.bats +8 -0
- data/test/integration/default/bats/useful_dirs.bats +8 -0
- data/test/integration/default/bats/user.bats +9 -0
- data/test/integration/default/bats/verify_bosh.bats +13 -0
- data/test/integration/default/bats/verify_git.bats +18 -0
- metadata +342 -0
@@ -0,0 +1,168 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
module Bosh; module Providers; module Clients; end; end; end
|
4
|
+
|
5
|
+
require "bosh/providers/clients/fog_provider_client"
|
6
|
+
require "bosh/providers/constants/aws_constants"
|
7
|
+
|
8
|
+
class Bosh::Providers::Clients::AwsProviderClient < Bosh::Providers::Clients::FogProviderClient
|
9
|
+
include Bosh::Providers::Constants::AwsConstants
|
10
|
+
|
11
|
+
# @return [Integer] megabytes of RAM for requested flavor of server
|
12
|
+
def ram_for_server_flavor(server_flavor_id)
|
13
|
+
if flavor = fog_compute_flavor(server_flavor_id)
|
14
|
+
flavor[:ram]
|
15
|
+
else
|
16
|
+
raise "Unknown AWS flavor '#{server_flavor_id}'"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Hash] e.g. { :bits => 0, :cores => 2, :disk => 0,
|
21
|
+
# :id => 't1.micro', :name => 'Micro Instance', :ram => 613}
|
22
|
+
# or nil if +server_flavor_id+ is not a supported flavor ID
|
23
|
+
def fog_compute_flavor(server_flavor_id)
|
24
|
+
aws_compute_flavors.find { |fl| fl[:id] == server_flavor_id }
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Array] of [Hash] for each supported compute flavor
|
28
|
+
# Example [Hash] { :bits => 0, :cores => 2, :disk => 0,
|
29
|
+
# :id => 't1.micro', :name => 'Micro Instance', :ram => 613}
|
30
|
+
def aws_compute_flavors
|
31
|
+
Fog::Compute::AWS::FLAVORS
|
32
|
+
end
|
33
|
+
|
34
|
+
def aws_compute_flavor_ids
|
35
|
+
aws_compute_flavors.map { |fl| fl[:id] }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Provision an EC2 or VPC elastic IP addess.
|
39
|
+
# * VPC - provision_public_ip_address(vpc: true)
|
40
|
+
# * EC2 - provision_public_ip_address
|
41
|
+
# @return [String] provisions a new public IP address in target region
|
42
|
+
# TODO nil if none available
|
43
|
+
def provision_public_ip_address(options={})
|
44
|
+
if options.delete(:vpc)
|
45
|
+
options[:domain] = "vpc"
|
46
|
+
else
|
47
|
+
options[:domain] = options.delete(:domain) || "standard"
|
48
|
+
end
|
49
|
+
address = fog_compute.addresses.create(options)
|
50
|
+
address.public_ip
|
51
|
+
# TODO catch error and return nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def associate_ip_address_with_server(ip_address, server)
|
55
|
+
address = fog_compute.addresses.get(ip_address)
|
56
|
+
address.server = server
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_vpc(name, cidr_block)
|
60
|
+
vpc = fog_compute.vpcs.create(name: name, cidr_block: cidr_block)
|
61
|
+
vpc.id
|
62
|
+
end
|
63
|
+
|
64
|
+
# Creates a VPC subnet
|
65
|
+
# @return [String] the subnet_id
|
66
|
+
def create_subnet(vpc_id, cidr_block)
|
67
|
+
subnet = fog_compute.subnets.create(vpc_id: vpc_id, cidr_block: cidr_block)
|
68
|
+
subnet.subnet_id
|
69
|
+
end
|
70
|
+
|
71
|
+
def create_internet_gateway(vpc_id)
|
72
|
+
gateway = fog_compute.internet_gateways.create(vpc_id: vpc_id)
|
73
|
+
gateway.id
|
74
|
+
end
|
75
|
+
|
76
|
+
def find_server_device(server, device)
|
77
|
+
server.volumes.all.find {|v| v.device == device}
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_and_attach_volume(name, disk_size, server, device)
|
81
|
+
volume = fog_compute.volumes.create(
|
82
|
+
size: disk_size,
|
83
|
+
name: name,
|
84
|
+
description: '',
|
85
|
+
device: device,
|
86
|
+
availability_zone: server.availability_zone)
|
87
|
+
# TODO: the following works in fog 1.9.0+ (but which has a bug in bootstrap)
|
88
|
+
# https://github.com/fog/fog/issues/1516
|
89
|
+
#
|
90
|
+
# volume.wait_for { volume.status == 'available' }
|
91
|
+
# volume.attach(server.id, "/dev/vdc")
|
92
|
+
# volume.wait_for { volume.status == 'in-use' }
|
93
|
+
#
|
94
|
+
# Instead, using:
|
95
|
+
volume.server = server
|
96
|
+
end
|
97
|
+
|
98
|
+
# Ubuntu 13.04
|
99
|
+
def raring_image_id(region=nil)
|
100
|
+
region = fog_compute.region
|
101
|
+
# http://cloud-images.ubuntu.com/locator/ec2/
|
102
|
+
image_id = case region.to_s
|
103
|
+
when "ap-northeast-1"
|
104
|
+
"ami-6b26ab6a"
|
105
|
+
when "ap-southeast-1"
|
106
|
+
"ami-2b511e79"
|
107
|
+
when "eu-west-1"
|
108
|
+
"ami-3d160149"
|
109
|
+
when "sa-east-1"
|
110
|
+
"ami-28e43e35"
|
111
|
+
when "us-east-1"
|
112
|
+
"ami-c30360aa"
|
113
|
+
when "us-west-1"
|
114
|
+
"ami-d383af96"
|
115
|
+
when "ap-southeast-2"
|
116
|
+
"ami-84a333be"
|
117
|
+
when "us-west-2"
|
118
|
+
"ami-bf1d8a8f"
|
119
|
+
end
|
120
|
+
image_id || raise("Please add Ubuntu 13.04 64bit (EBS) AMI image id to aws.rb#raring_image_id method for region '#{region}'")
|
121
|
+
end
|
122
|
+
|
123
|
+
def bootstrap(new_attributes = {})
|
124
|
+
new_attributes[:image_id] ||= raring_image_id(fog_compute.region)
|
125
|
+
vpc = new_attributes[:subnet_id]
|
126
|
+
|
127
|
+
server = fog_compute.servers.new(new_attributes)
|
128
|
+
|
129
|
+
unless new_attributes[:key_name]
|
130
|
+
raise "please provide :key_name attribute"
|
131
|
+
end
|
132
|
+
unless private_key_path = new_attributes.delete(:private_key_path)
|
133
|
+
raise "please provide :private_key_path attribute"
|
134
|
+
end
|
135
|
+
|
136
|
+
if vpc
|
137
|
+
# TODO setup security group on new server
|
138
|
+
else
|
139
|
+
# make sure port 22 is open in the first security group
|
140
|
+
security_group = fog_compute.security_groups.get(server.groups.first)
|
141
|
+
authorized = security_group.ip_permissions.detect do |ip_permission|
|
142
|
+
ip_permission['ipRanges'].first && ip_permission['ipRanges'].first['cidrIp'] == '0.0.0.0/0' &&
|
143
|
+
ip_permission['fromPort'] == 22 &&
|
144
|
+
ip_permission['ipProtocol'] == 'tcp' &&
|
145
|
+
ip_permission['toPort'] == 22
|
146
|
+
end
|
147
|
+
unless authorized
|
148
|
+
security_group.authorize_port_range(22..22)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
server.save
|
153
|
+
unless Fog.mocking?
|
154
|
+
server.wait_for { ready? }
|
155
|
+
server.setup(:keys => [private_key_path])
|
156
|
+
end
|
157
|
+
server
|
158
|
+
end
|
159
|
+
|
160
|
+
# Construct a Fog::Compute object
|
161
|
+
# Uses +attributes+ which normally originates from +settings.provider+
|
162
|
+
def setup_fog_connection
|
163
|
+
configuration = Fog.symbolize_credentials(attributes.credentials)
|
164
|
+
configuration[:provider] = "AWS"
|
165
|
+
configuration[:region] = attributes.region
|
166
|
+
@fog_compute = Fog::Compute.new(configuration)
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
require "fog"
|
4
|
+
module Bosh; module Providers; module Clients; end; end; end
|
5
|
+
|
6
|
+
class Bosh::Providers::Clients::FogProviderClient
|
7
|
+
attr_reader :fog_compute
|
8
|
+
attr_reader :attributes
|
9
|
+
|
10
|
+
def initialize(attributes)
|
11
|
+
@attributes = attributes.is_a?(Hash) ? Settingslogic.new(attributes) : attributes
|
12
|
+
raise "@attributes must be Settingslogic (or Hash)" unless @attributes.is_a?(Settingslogic)
|
13
|
+
setup_fog_connection
|
14
|
+
end
|
15
|
+
|
16
|
+
def setup_fog_connection
|
17
|
+
raise "must implement"
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_key_pair(key_pair_name)
|
21
|
+
fog_compute.key_pairs.create(:name => key_pair_name)
|
22
|
+
end
|
23
|
+
|
24
|
+
# set_resource_name(fog_server, "inception")
|
25
|
+
# set_resource_name(volume, "inception-root")
|
26
|
+
# set_resource_name(volume, "inception-store")
|
27
|
+
def set_resource_name(resource, name)
|
28
|
+
fog_compute.tags.create :key => "Name", :value => name, :resource_id => resource.id
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete_key_pair_if_exists(key_pair_name)
|
32
|
+
if fog_key_pair = fog_compute.key_pairs.get(key_pair_name)
|
33
|
+
fog_key_pair.destroy
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete_servers_with_name(name)
|
38
|
+
fog_compute.servers.select {|s| s.tags["Name"].downcase == name.downcase }.each do |server|
|
39
|
+
puts "Destroying server #{server.id}..."
|
40
|
+
server.destroy
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def delete_volumes_with_name(name)
|
45
|
+
fog_compute.volumes.select do |v|
|
46
|
+
volume_name = v.tags["Name"]
|
47
|
+
volume_name && volume_name.downcase == name.downcase
|
48
|
+
end.each do |volume|
|
49
|
+
puts "Destroying volume #{volume.id}..."
|
50
|
+
volume.destroy
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Destroy all IP addresses that aren't bound to a server
|
55
|
+
def cleanup_unused_ip_addresses
|
56
|
+
fog_compute.addresses.each do |a|
|
57
|
+
unless a.server
|
58
|
+
puts "Deleting unused IP address #{a.public_ip}..."
|
59
|
+
a.destroy
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Creates or reuses an security group and opens ports.
|
65
|
+
#
|
66
|
+
# +security_group_name+ is the name to be created or reused
|
67
|
+
# +ports+ is a hash of name/port for ports to open, for example:
|
68
|
+
# {
|
69
|
+
# ssh: 22,
|
70
|
+
# http: 80,
|
71
|
+
# https: 443
|
72
|
+
# }
|
73
|
+
# protocol defaults to TCP
|
74
|
+
# You can also use a more verbose +ports+ using the format:
|
75
|
+
# {
|
76
|
+
# ssh: 22,
|
77
|
+
# http: { ports: (80..82) },
|
78
|
+
# mosh: { protocol: "udp", ports: (60000..60050) }
|
79
|
+
# mosh: { protocol: "rdp", ports: (3398..3398), ip_ranges: [ { cidrIp: "196.212.12.34/32" } ] }
|
80
|
+
# }
|
81
|
+
# In this example,
|
82
|
+
# * TCP 22 will be opened for ssh from any ip_range,
|
83
|
+
# * TCP ports 80, 81, 82 for http from any ip_range,
|
84
|
+
# * UDP 60000 -> 60050 for mosh from any ip_range and
|
85
|
+
# * TCP 3398 for RDP from ip range: 96.212.12.34/32
|
86
|
+
def create_security_group(security_group_name, description, ports)
|
87
|
+
security_groups = fog_compute.security_groups
|
88
|
+
unless sg = security_groups.find { |s| s.name == security_group_name }
|
89
|
+
sg = fog_compute.security_groups.create(name: security_group_name, description: description)
|
90
|
+
puts "Created security group #{security_group_name}"
|
91
|
+
else
|
92
|
+
puts "Reusing security group #{security_group_name}"
|
93
|
+
end
|
94
|
+
ip_permissions = ip_permissions(sg)
|
95
|
+
ports_opened = 0
|
96
|
+
ports.each do |name, port_defn|
|
97
|
+
(protocol, port_range, ip_range) = extract_port_definition(port_defn)
|
98
|
+
unless port_open?(ip_permissions, port_range, protocol, ip_range)
|
99
|
+
authorize_port_range(sg, port_range, protocol, ip_range)
|
100
|
+
puts " -> opened #{name} ports #{protocol.upcase} #{port_range.min}..#{port_range.max} from IP range #{ip_range}"
|
101
|
+
ports_opened += 1
|
102
|
+
end
|
103
|
+
end
|
104
|
+
puts " -> no additional ports opened" if ports_opened == 0
|
105
|
+
true
|
106
|
+
end
|
107
|
+
|
108
|
+
def port_open?(ip_permissions, port_range, protocol, ip_range)
|
109
|
+
ip_permissions && ip_permissions.find do |ip|
|
110
|
+
ip["ipProtocol"] == protocol \
|
111
|
+
&& ip["ipRanges"].detect { |range| range["cidrIp"] == ip_range } \
|
112
|
+
&& ip["fromPort"] <= port_range.min \
|
113
|
+
&& ip["toPort"] >= port_range.max
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def authorize_port_range(sg, port_range, protocol, ip_range)
|
118
|
+
sg.authorize_port_range(port_range, {:ip_protocol => protocol, :cidr_ip => ip_range})
|
119
|
+
end
|
120
|
+
|
121
|
+
def ip_permissions(sg)
|
122
|
+
sg.ip_permissions
|
123
|
+
end
|
124
|
+
|
125
|
+
# Any of the following +port_defn+ can be used:
|
126
|
+
# {
|
127
|
+
# ssh: 22,
|
128
|
+
# http: { ports: (80..82) },
|
129
|
+
# mosh: { protocol: "udp", ports: (60000..60050) }
|
130
|
+
# mosh: { protocol: "rdp", ports: (3398..3398), ip_range: "196.212.12.34/32" }
|
131
|
+
# }
|
132
|
+
# In this example,
|
133
|
+
# * TCP 22 will be opened for ssh from any ip_range,
|
134
|
+
# * TCP ports 80, 81, 82 for http from any ip_range,
|
135
|
+
# * UDP 60000 -> 60050 for mosh from any ip_range and
|
136
|
+
# * TCP 3398 for RDP from ip range: 96.212.12.34/32
|
137
|
+
def extract_port_definition(port_defn)
|
138
|
+
protocol = "tcp"
|
139
|
+
ip_range = "0.0.0.0/0"
|
140
|
+
if port_defn.is_a? Integer
|
141
|
+
port_range = (port_defn..port_defn)
|
142
|
+
elsif port_defn.is_a? Range
|
143
|
+
port_range = port_defn
|
144
|
+
elsif port_defn.is_a? Hash
|
145
|
+
protocol = port_defn[:protocol] if port_defn[:protocol]
|
146
|
+
port_range = port_defn[:ports] if port_defn[:ports]
|
147
|
+
ip_range = port_defn[:ip_range] if port_defn[:ip_range]
|
148
|
+
end
|
149
|
+
[protocol, port_range, ip_range]
|
150
|
+
end
|
151
|
+
|
152
|
+
def provision_or_reuse_public_ip_address(options={})
|
153
|
+
provision_public_ip_address(options) || find_unused_public_ip_address(options)
|
154
|
+
end
|
155
|
+
|
156
|
+
def find_unused_public_ip_address(options={})
|
157
|
+
if address = fog_compute.addresses.find { |s| s.server_id.nil? }
|
158
|
+
address.public_ip
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
module Bosh; module Providers; module Clients; end; end; end
|
4
|
+
|
5
|
+
require "bosh/providers/clients/fog_provider_client"
|
6
|
+
require "bosh/providers/constants/openstack_constants"
|
7
|
+
|
8
|
+
class Bosh::Providers::Clients::OpenStackProviderClient < Bosh::Providers::Clients::FogProviderClient
|
9
|
+
# @return [String] provisions a new public IP address in target region
|
10
|
+
# TODO nil if none available
|
11
|
+
def provision_public_ip_address(options={})
|
12
|
+
address = fog_compute.addresses.create
|
13
|
+
address.ip
|
14
|
+
# TODO catch error and return nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def associate_ip_address_with_server(ip_address, server)
|
18
|
+
address = fog_compute.addresses.find { |a| a.ip == ip_address }
|
19
|
+
address.server = server
|
20
|
+
end
|
21
|
+
|
22
|
+
# Hook method for FogProviderClient#create_security_group
|
23
|
+
def ip_permissions(sg)
|
24
|
+
sg.rules
|
25
|
+
end
|
26
|
+
|
27
|
+
# Hook method for FogProviderClient#create_security_group
|
28
|
+
def authorize_port_range(sg, port_range, protocol, ip_range)
|
29
|
+
sg.create_security_group_rule(port_range.min, port_range.max, protocol, ip_range)
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_server_device(server, device)
|
33
|
+
va = fog_compute.get_server_volumes(server.id).body['volumeAttachments']
|
34
|
+
va.find { |v| v["device"] == device }
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_and_attach_volume(name, disk_size, server, device)
|
38
|
+
volume = fog_compute.volumes.create(:name => name,
|
39
|
+
:description => "",
|
40
|
+
:size => disk_size,
|
41
|
+
:availability_zone => server.availability_zone)
|
42
|
+
volume.wait_for { volume.status == 'available' }
|
43
|
+
volume.attach(server.id, device)
|
44
|
+
volume.wait_for { volume.status == 'in-use' }
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete_security_group_and_servers(sg_name)
|
48
|
+
raise "not implemented yet"
|
49
|
+
end
|
50
|
+
|
51
|
+
# Construct a Fog::Compute object
|
52
|
+
# Uses +attributes+ which normally originates from +settings.provider+
|
53
|
+
def setup_fog_connection
|
54
|
+
configuration = Fog.symbolize_credentials(attributes.credentials)
|
55
|
+
configuration[:provider] = "OpenStack"
|
56
|
+
unless attributes.region == openstack_constants.no_region_code
|
57
|
+
configuration[:openstack_region] = attributes.region
|
58
|
+
end
|
59
|
+
@fog_compute = Fog::Compute.new(configuration)
|
60
|
+
end
|
61
|
+
|
62
|
+
def openstack_constants
|
63
|
+
Bosh::Providers::Constants::OpenStackConstants
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
module Bosh; module Providers; module Constants; end; end; end
|
4
|
+
|
5
|
+
module Bosh::Providers::Constants::AwsConstants
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# http://docs.aws.amazon.com/general/latest/gr/rande.html#region
|
9
|
+
def region_labels
|
10
|
+
[
|
11
|
+
{ label: "US East (Northern Virginia) Region", code: "us-east-1" },
|
12
|
+
{ label: "US West (Oregon) Region", code: "us-west-2" },
|
13
|
+
{ label: "US West (Northern California) Region", code: "us-west-1" },
|
14
|
+
{ label: "EU (Ireland) Region", code: "eu-west-1" },
|
15
|
+
{ label: "Asia Pacific (Singapore) Region", code: "ap-southeast-1" },
|
16
|
+
{ label: "Asia Pacific (Sydney) Region", code: "ap-southeast-2" },
|
17
|
+
{ label: "Asia Pacific (Tokyo) Region", code: "ap-northeast-1" },
|
18
|
+
{ label: "South America (Sao Paulo) Region", code: "sa-east-1" },
|
19
|
+
]
|
20
|
+
end
|
21
|
+
|
22
|
+
def default_region_code
|
23
|
+
"us-east-1"
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
module Bosh; module Providers; module Constants; end; end; end
|
4
|
+
|
5
|
+
module Bosh::Providers::Constants::OpenStackConstants
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# explicit value representing "no region requested"
|
9
|
+
def no_region_code
|
10
|
+
"no-region-requested"
|
11
|
+
end
|
12
|
+
end
|
data/lib/inception.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
require "thor"
|
2
|
+
require "highline"
|
3
|
+
require "fileutils"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
# for the #sh helper
|
7
|
+
require "rake"
|
8
|
+
require "rake/file_utils"
|
9
|
+
|
10
|
+
require "escape"
|
11
|
+
require "inception/cli_helpers/display"
|
12
|
+
require "inception/cli_helpers/infrastructure"
|
13
|
+
require "inception/cli_helpers/interactions"
|
14
|
+
require "inception/cli_helpers/provider"
|
15
|
+
require "inception/cli_helpers/settings"
|
16
|
+
require "inception/cli_helpers/prepare_deploy_settings"
|
17
|
+
|
18
|
+
module Inception
|
19
|
+
class Cli < Thor
|
20
|
+
include FileUtils
|
21
|
+
include Inception::CliHelpers::Display
|
22
|
+
include Inception::CliHelpers::Infrastructure
|
23
|
+
include Inception::CliHelpers::Interactions
|
24
|
+
include Inception::CliHelpers::Provider
|
25
|
+
include Inception::CliHelpers::Settings
|
26
|
+
include Inception::CliHelpers::PrepareDeploySettings
|
27
|
+
|
28
|
+
desc "deploy", "Create/upgrade a Bosh inception server"
|
29
|
+
def deploy
|
30
|
+
migrate_old_settings
|
31
|
+
configure_provider
|
32
|
+
prepare_deploy_settings
|
33
|
+
perform_deploy
|
34
|
+
converge_cookbooks
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "delete", "Destroy target Bosh inception server, volumes & release the IP address"
|
38
|
+
method_option :"non-interactive", aliases: ["-n"], type: :boolean, desc: "Don't ask questions, just get crankin'"
|
39
|
+
def delete
|
40
|
+
migrate_old_settings
|
41
|
+
perform_delete(options[:"non-interactive"])
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "ssh [COMMAND]", "Open an ssh session to the inception server [do nothing if local machine is the inception server]"
|
45
|
+
long_desc <<-DESC
|
46
|
+
If a command is supplied, it will be run, otherwise a session will be opened.
|
47
|
+
DESC
|
48
|
+
def ssh(cmd=nil)
|
49
|
+
migrate_old_settings
|
50
|
+
run_ssh_command_or_open_tunnel(cmd)
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "tmux", "Open an ssh (with tmux) session to the inception server [do nothing if local machine is inception server]"
|
54
|
+
long_desc <<-DESC
|
55
|
+
Opens a connection using ssh and attaches to the most recent tmux session;
|
56
|
+
giving you persistance across disconnects.
|
57
|
+
DESC
|
58
|
+
def tmux
|
59
|
+
migrate_old_settings
|
60
|
+
run_ssh_command_or_open_tunnel(["-t", "tmux attach || tmux new-session"])
|
61
|
+
end
|
62
|
+
|
63
|
+
no_tasks do
|
64
|
+
# update settings.git.name/git.email from local ~/.gitconfig if available
|
65
|
+
# provision public IP address for inception server if not allocated one
|
66
|
+
# Note: helper methods are in inception/cli_helpers/prepare_deploy_settings.rb
|
67
|
+
def prepare_deploy_settings
|
68
|
+
header "Preparing deployment settings"
|
69
|
+
update_git_config
|
70
|
+
provision_or_reuse_public_ip_address_for_inception unless settings.exists?("inception.provisioned.ip_address")
|
71
|
+
recreate_key_pair_for_inception unless settings.exists?("inception.key_pair.private_key")
|
72
|
+
recreate_private_key_file_for_inception
|
73
|
+
validate_deploy_settings
|
74
|
+
setup_next_deploy_actions
|
75
|
+
end
|
76
|
+
|
77
|
+
def perform_deploy
|
78
|
+
header "Provision inception server"
|
79
|
+
server = InceptionServer.new(provider_client, settings.inception, settings_ssh_dir)
|
80
|
+
server.create
|
81
|
+
ensure
|
82
|
+
# after any error handling, still save the current InceptionServer state back into settings.inception
|
83
|
+
settings["inception"] = server.export_attributes
|
84
|
+
save_settings!
|
85
|
+
end
|
86
|
+
|
87
|
+
def setup_next_deploy_actions
|
88
|
+
settings["next_deploy_actions"] ||= {}
|
89
|
+
@next_deploy_actions = NextDeployActions.new(settings.next_deploy_actions, options)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Perform converge chef cookbooks upon inception server
|
93
|
+
# Does not update settings
|
94
|
+
def converge_cookbooks
|
95
|
+
if @next_deploy_actions.skip_chef_converge?
|
96
|
+
header "Prepare inception server", skip: "Requested to be skipped on this deploy."
|
97
|
+
else
|
98
|
+
header "Prepare inception server"
|
99
|
+
server = InceptionServer.new(provider_client, settings.inception, settings_ssh_dir)
|
100
|
+
cookbook = InceptionServerCookbook.new(server, settings, settings_dir)
|
101
|
+
cookbook.prepare
|
102
|
+
settings.set("cookbook.prepared", true)
|
103
|
+
save_settings!
|
104
|
+
cookbook.converge
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def perform_delete(non_interactive)
|
109
|
+
server = InceptionServer.new(provider_client, settings.inception, settings_ssh_dir)
|
110
|
+
if non_interactive
|
111
|
+
header "Deleting inception server, volumes and releasing IP address"
|
112
|
+
server.delete_all
|
113
|
+
else
|
114
|
+
raise "Interactive delete not implemented yet"
|
115
|
+
end
|
116
|
+
ensure
|
117
|
+
# after any error handling, still save the current InceptionServer state back into settings.inception
|
118
|
+
settings["inception"] = server.export_attributes
|
119
|
+
save_settings!
|
120
|
+
end
|
121
|
+
|
122
|
+
def run_ssh_command_or_open_tunnel(cmd)
|
123
|
+
recreate_private_key_file_for_inception
|
124
|
+
unless settings.exists?("inception.provisioned.host")
|
125
|
+
exit "inception server has not finished launching; run to complete: inception deploy"
|
126
|
+
end
|
127
|
+
|
128
|
+
server = InceptionServer.new(provider_client, settings.inception, settings_ssh_dir)
|
129
|
+
username = settings.inception.provisioned.username
|
130
|
+
host = settings.inception.provisioned.host
|
131
|
+
result = system Escape.shell_command(["ssh", "-i", server.private_key_path, "#{username}@#{host}", cmd].flatten.compact)
|
132
|
+
exit result
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|