inception-server 0.2.1
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 +21 -0
- data/.kitchen.yml +47 -0
- data/.rspec +3 -0
- data/.travis.yml +18 -0
- data/Berksfile +8 -0
- data/Berksfile.lock +9 -0
- data/ChangeLog.md +20 -0
- data/Gemfile +27 -0
- data/Guardfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +126 -0
- data/Rakefile +66 -0
- data/TODO.md +25 -0
- data/bin/inception +8 -0
- data/bin/inception-server +8 -0
- data/config/ssh/kitchen-aws +23 -0
- data/cookbooks/bosh_inception/README.md +15 -0
- data/cookbooks/bosh_inception/attributes/default.rb +25 -0
- data/cookbooks/bosh_inception/files/default/Gemfile.cf +5 -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 +16 -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_dotfog.rb +29 -0
- data/cookbooks/bosh_inception/recipes/setup_git.rb +34 -0
- data/cookbooks/bosh_inception/recipes/useful_dirs.rb +13 -0
- data/inception-server.gemspec +43 -0
- data/lib/inception/cli.rb +141 -0
- data/lib/inception/cli_helpers/display.rb +26 -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 +53 -0
- data/lib/inception/inception_server.rb +304 -0
- data/lib/inception/inception_server_cookbook.rb +90 -0
- data/lib/inception/next_deploy_actions.rb +20 -0
- data/lib/inception/providers/README.md +5 -0
- data/lib/inception/providers/clients/aws_provider_client.rb +144 -0
- data/lib/inception/providers/clients/fog_provider_client.rb +185 -0
- data/lib/inception/providers/clients/openstack_provider_client.rb +84 -0
- data/lib/inception/providers/constants/aws_constants.rb +25 -0
- data/lib/inception/providers/constants/openstack_constants.rb +12 -0
- data/lib/inception/providers.rb +28 -0
- data/lib/inception/version.rb +3 -0
- data/lib/inception.rb +9 -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 +38 -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/cli_delete_spec.rb +39 -0
- data/spec/unit/cli_deploy_aws_spec.rb +83 -0
- data/spec/unit/cli_ssh_spec.rb +80 -0
- data/spec/unit/inception_server_cookbook_spec.rb +62 -0
- data/spec/unit/inception_server_spec.rb +58 -0
- data/spec/unit/providers/aws_spec.rb +198 -0
- data/test/integration/default/bats/discover_user.bash +2 -0
- data/test/integration/default/bats/dotfog.bats +11 -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 +18 -0
- data/test/integration/default/bats/verify_git.bats +18 -0
- metadata +361 -0
@@ -0,0 +1,185 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
require "fog"
|
4
|
+
module Inception; module Providers; module Clients; end; end; end
|
5
|
+
|
6
|
+
class Inception::Providers::Clients::FogProviderClient
|
7
|
+
attr_reader :fog_compute
|
8
|
+
attr_reader :attributes
|
9
|
+
|
10
|
+
def initialize(attributes)
|
11
|
+
@attributes = attributes.is_a?(Hash) ? ReadWriteSettings.new(attributes) : attributes
|
12
|
+
raise "@attributes must be ReadWriteSettings (or Hash)" unless @attributes.is_a?(ReadWriteSettings)
|
13
|
+
setup_fog_connection
|
14
|
+
end
|
15
|
+
|
16
|
+
def flavor_id(flavor_name)
|
17
|
+
if flavor = fog_compute.flavors.find { |fl| fl.name == flavor_name }
|
18
|
+
flavor.id
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def setup_fog_connection
|
23
|
+
raise "must implement"
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_key_pair(key_pair_name)
|
27
|
+
fog_compute.key_pairs.create(:name => key_pair_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
# set_resource_name(fog_server, "inception")
|
31
|
+
# set_resource_name(volume, "inception-root")
|
32
|
+
# set_resource_name(volume, "inception-store")
|
33
|
+
def set_resource_name(resource, name)
|
34
|
+
fog_compute.tags.create :key => "Name", :value => name, :resource_id => resource.id
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete_key_pair_if_exists(key_pair_name)
|
38
|
+
if fog_key_pair = fog_compute.key_pairs.get(key_pair_name)
|
39
|
+
fog_key_pair.destroy
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete_servers_with_name(name)
|
44
|
+
fog_compute.servers.select {|s| s.tags["Name"] && (s.tags["Name"].downcase == name.downcase) }.each do |server|
|
45
|
+
puts "Destroying server #{server.id}... "
|
46
|
+
server.destroy
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def delete_volumes_with_name(name)
|
51
|
+
fog_compute.volumes.select do |v|
|
52
|
+
volume_name = v.tags["Name"]
|
53
|
+
volume_name && volume_name.downcase == name.downcase
|
54
|
+
end.each do |volume|
|
55
|
+
puts "Destroying volume #{volume.id}... "
|
56
|
+
volume.destroy
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Destroy all IP addresses that aren't bound to a server
|
61
|
+
def cleanup_unused_ip_addresses
|
62
|
+
fog_compute.addresses.each do |a|
|
63
|
+
unless a.server
|
64
|
+
puts "Deleting unused IP address #{a.public_ip}... "
|
65
|
+
a.destroy
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Creates or reuses an security group and opens ports.
|
71
|
+
#
|
72
|
+
# +security_group_name+ is the name to be created or reused
|
73
|
+
# +ports+ is a hash of name/port for ports to open, for example:
|
74
|
+
# {
|
75
|
+
# ssh: 22,
|
76
|
+
# http: 80,
|
77
|
+
# https: 443
|
78
|
+
# }
|
79
|
+
# protocol defaults to TCP
|
80
|
+
# You can also use a more verbose +ports+ using the format:
|
81
|
+
# {
|
82
|
+
# ssh: 22,
|
83
|
+
# http: { ports: (80..82) },
|
84
|
+
# mosh: { protocol: "udp", ports: (60000..60050) }
|
85
|
+
# mosh: { protocol: "rdp", ports: (3398..3398), ip_ranges: [ { cidrIp: "196.212.12.34/32" } ] }
|
86
|
+
# }
|
87
|
+
# In this example,
|
88
|
+
# * TCP 22 will be opened for ssh from any ip_range,
|
89
|
+
# * TCP ports 80, 81, 82 for http from any ip_range,
|
90
|
+
# * UDP 60000 -> 60050 for mosh from any ip_range and
|
91
|
+
# * TCP 3398 for RDP from ip range: 96.212.12.34/32
|
92
|
+
def create_security_group(security_group_name, description, ports)
|
93
|
+
security_groups = fog_compute.security_groups
|
94
|
+
unless sg = security_groups.find { |s| s.name == security_group_name }
|
95
|
+
sg = fog_compute.security_groups.create(name: security_group_name, description: description)
|
96
|
+
puts "Created security group #{security_group_name}"
|
97
|
+
ip_permissions = ip_permissions(sg)
|
98
|
+
ports_opened = 0
|
99
|
+
ports.each do |name, port_defn|
|
100
|
+
(protocol, port_range, ip_range) = extract_port_definition(port_defn)
|
101
|
+
unless port_open?(ip_permissions, port_range, protocol, ip_range)
|
102
|
+
authorize_port_range(sg, port_range, protocol, ip_range)
|
103
|
+
puts " -> opened #{name} ports #{protocol.upcase} #{port_range.min}..#{port_range.max} from IP range #{ip_range}"
|
104
|
+
ports_opened += 1
|
105
|
+
end
|
106
|
+
end
|
107
|
+
else
|
108
|
+
puts "Reusing security group #{security_group_name}"
|
109
|
+
end
|
110
|
+
true
|
111
|
+
end
|
112
|
+
|
113
|
+
def port_open?(ip_permissions, port_range, protocol, ip_range)
|
114
|
+
ip_permissions && ip_permissions.find do |ip|
|
115
|
+
ip["ipProtocol"] == protocol \
|
116
|
+
&& ip["ipRanges"].detect { |range| range["cidrIp"] == ip_range } \
|
117
|
+
&& ip["fromPort"] <= port_range.min \
|
118
|
+
&& ip["toPort"] >= port_range.max
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def authorize_port_range(sg, port_range, protocol, ip_range)
|
123
|
+
sg.authorize_port_range(port_range, {:ip_protocol => protocol, :cidr_ip => ip_range})
|
124
|
+
end
|
125
|
+
|
126
|
+
def ip_permissions(sg)
|
127
|
+
sg.ip_permissions
|
128
|
+
end
|
129
|
+
|
130
|
+
# Any of the following +port_defn+ can be used:
|
131
|
+
# {
|
132
|
+
# ssh: 22,
|
133
|
+
# http: { ports: (80..82) },
|
134
|
+
# mosh: { protocol: "udp", ports: (60000..60050) }
|
135
|
+
# mosh: { protocol: "rdp", ports: (3398..3398), ip_range: "196.212.12.34/32" }
|
136
|
+
# }
|
137
|
+
# In this example,
|
138
|
+
# * TCP 22 will be opened for ssh from any ip_range,
|
139
|
+
# * TCP ports 80, 81, 82 for http from any ip_range,
|
140
|
+
# * UDP 60000 -> 60050 for mosh from any ip_range and
|
141
|
+
# * TCP 3398 for RDP from ip range: 96.212.12.34/32
|
142
|
+
def extract_port_definition(port_defn)
|
143
|
+
protocol = "tcp"
|
144
|
+
ip_range = "0.0.0.0/0"
|
145
|
+
if port_defn.is_a? Integer
|
146
|
+
port_range = (port_defn..port_defn)
|
147
|
+
elsif port_defn.is_a? Range
|
148
|
+
port_range = port_defn
|
149
|
+
elsif port_defn.is_a? Hash
|
150
|
+
protocol = port_defn[:protocol] if port_defn[:protocol]
|
151
|
+
port_range = port_defn[:ports] if port_defn[:ports]
|
152
|
+
ip_range = port_defn[:ip_range] if port_defn[:ip_range]
|
153
|
+
end
|
154
|
+
[protocol, port_range, ip_range]
|
155
|
+
end
|
156
|
+
|
157
|
+
def provision_or_reuse_public_ip_address(options={})
|
158
|
+
provision_public_ip_address(options) || find_unused_public_ip_address(options)
|
159
|
+
end
|
160
|
+
|
161
|
+
def find_unused_public_ip_address(options={})
|
162
|
+
if address = fog_compute.addresses.find { |s| s.server_id.nil? }
|
163
|
+
address.public_ip
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def bootstrap(new_attributes)
|
168
|
+
server = fog_compute.servers.new(new_attributes)
|
169
|
+
|
170
|
+
unless new_attributes[:key_name]
|
171
|
+
raise "please provide :key_name attribute"
|
172
|
+
end
|
173
|
+
unless private_key_path = new_attributes.delete(:private_key_path)
|
174
|
+
raise "please provide :private_key_path attribute"
|
175
|
+
end
|
176
|
+
|
177
|
+
server.save
|
178
|
+
unless Fog.mocking?
|
179
|
+
server.wait_for { ready? }
|
180
|
+
server.setup(keys: [private_key_path])
|
181
|
+
end
|
182
|
+
server
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
module Inception; module Providers; module Clients; end; end; end
|
4
|
+
|
5
|
+
require "inception/providers/clients/fog_provider_client"
|
6
|
+
require "inception/providers/constants/openstack_constants"
|
7
|
+
|
8
|
+
class Inception::Providers::Clients::OpenStackProviderClient < Inception::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 image_id
|
48
|
+
raise "Not yet implemented"
|
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
|
+
@fog_compute = Fog::Compute.new(configuration)
|
57
|
+
end
|
58
|
+
|
59
|
+
def fog_attributes(inception_server)
|
60
|
+
# :name => "Inception VM",
|
61
|
+
# :key_name => key_name,
|
62
|
+
# :private_key_path => inception_vm_private_key_path,
|
63
|
+
# :flavor_ref => inception_flavor.id,
|
64
|
+
# :image_ref => inception_image.id,
|
65
|
+
# :security_groups => [settings["inception"]["security_group"]],
|
66
|
+
# :username => username
|
67
|
+
{
|
68
|
+
name: inception_server.server_name,
|
69
|
+
key_name: inception_server.key_name,
|
70
|
+
private_key_path: inception_server.private_key_path,
|
71
|
+
image_ref: inception_server.image_id,
|
72
|
+
flavor_ref: flavor_id(inception_server.flavor),
|
73
|
+
security_groups: inception_server.security_groups,
|
74
|
+
public_key: inception_server.public_key,
|
75
|
+
public_ip_address: inception_server.ip_address,
|
76
|
+
bits: 64,
|
77
|
+
username: "ubuntu",
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def openstack_constants
|
82
|
+
Inception::Providers::Constants::OpenStackConstants
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
module Inception; module Providers; module Constants; end; end; end
|
4
|
+
|
5
|
+
module Inception::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 Inception; module Providers; module Constants; end; end; end
|
4
|
+
|
5
|
+
module Inception::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
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
module Inception; end
|
4
|
+
|
5
|
+
module Inception::Providers
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# returns a BOSH provider (CPI) specific object
|
9
|
+
# with helpers related to that provider
|
10
|
+
# returns nil if +provider_name+ is unknown
|
11
|
+
def provider_client(attributes)
|
12
|
+
attributes = attributes.is_a?(Hash) ? ReadWriteSettings.new(attributes) : attributes
|
13
|
+
case attributes.name.to_sym
|
14
|
+
when :aws
|
15
|
+
@aws_provider_client ||= begin
|
16
|
+
require "inception/providers/clients/aws_provider_client"
|
17
|
+
Inception::Providers::Clients::AwsProviderClient.new(attributes)
|
18
|
+
end
|
19
|
+
when :openstack
|
20
|
+
@openstack_provider_client ||= begin
|
21
|
+
require "inception/providers/clients/openstack_provider_client"
|
22
|
+
Inception::Providers::Clients::OpenStackProviderClient.new(attributes)
|
23
|
+
end
|
24
|
+
else
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/inception.rb
ADDED
data/nodes/.gitkeep
ADDED
File without changes
|
File without changes
|
@@ -0,0 +1,14 @@
|
|
1
|
+
---
|
2
|
+
provider:
|
3
|
+
name: aws
|
4
|
+
region: us-west-2
|
5
|
+
credentials:
|
6
|
+
aws_access_key_id: MOCK_AWS_ACCESS_KEY_ID
|
7
|
+
aws_secret_access_key: MOCK_AWS_SECRET_ACCESS_KEY
|
8
|
+
inception:
|
9
|
+
ip_address: 54.214.15.178
|
10
|
+
key_pair:
|
11
|
+
name: inception
|
12
|
+
private_key: private_key
|
13
|
+
extensions:
|
14
|
+
- mosh
|
@@ -0,0 +1,31 @@
|
|
1
|
+
---
|
2
|
+
provider:
|
3
|
+
name: aws
|
4
|
+
region: us-west-2
|
5
|
+
credentials:
|
6
|
+
aws_access_key_id: MOCK_AWS_ACCESS_KEY_ID
|
7
|
+
aws_secret_access_key: MOCK_AWS_SECRET_ACCESS_KEY
|
8
|
+
git:
|
9
|
+
name: Dr Nic Williams
|
10
|
+
email: drnicwilliams@gmail.com
|
11
|
+
inception:
|
12
|
+
key_pair:
|
13
|
+
name: inception
|
14
|
+
private_key: private_key
|
15
|
+
extensions:
|
16
|
+
- mosh
|
17
|
+
size: m1.small
|
18
|
+
disk_size: 16
|
19
|
+
provisioned:
|
20
|
+
ip_address: 54.214.15.178
|
21
|
+
image_id: ami-123456
|
22
|
+
server_id: i-e7f005d2
|
23
|
+
security_groups:
|
24
|
+
- ssh
|
25
|
+
- mosh
|
26
|
+
username: vcap
|
27
|
+
disk_device:
|
28
|
+
volume_id: vol-123456
|
29
|
+
external: /dev/sdf
|
30
|
+
internal: /dev/xvdf
|
31
|
+
host: ec2-54-214-15-178.us-west-2.compute.amazonaws.com
|
File without changes
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.expand_path("../../../support/aws/aws_helpers", __FILE__)
|
2
|
+
|
3
|
+
describe "AWS deployment without Chef run" do
|
4
|
+
include FileUtils
|
5
|
+
include AwsHelpers
|
6
|
+
|
7
|
+
if AwsHelpers.aws_credentials?
|
8
|
+
before do
|
9
|
+
prepare_aws("basic", aws_region, "next_deploy_actions.no_converge" => true)
|
10
|
+
end
|
11
|
+
after(:all) do
|
12
|
+
destroy_test_constructs unless keep_after_test?
|
13
|
+
end
|
14
|
+
|
15
|
+
def aws_region
|
16
|
+
ENV['AWS_REGION'] || "us-west-2"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "creates an EC2 inception/microbosh with the associated resources" do
|
20
|
+
create_manifest
|
21
|
+
|
22
|
+
manifest_file = home_file(".inception_server", "settings.yml")
|
23
|
+
File.should be_exists(manifest_file)
|
24
|
+
|
25
|
+
cmd.deploy
|
26
|
+
|
27
|
+
inception_servers = fog.servers.select { |s| s.tags["Name"] == test_server_name && s.ready? }
|
28
|
+
inception_servers.size.should == 1
|
29
|
+
|
30
|
+
server = inception_servers.first
|
31
|
+
server.volumes.size.should == 2
|
32
|
+
named_volume = server.volumes.select { |s| s.tags["Name"] == test_server_name }
|
33
|
+
named_volume.should_not be_nil
|
34
|
+
end
|
35
|
+
else
|
36
|
+
it "no AWS integration specs run; missing $AWS_ACCESS_KEY_ID etc"
|
37
|
+
end
|
38
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require "bundler"
|
7
|
+
Bundler.setup(:default, :test)
|
8
|
+
|
9
|
+
$:.unshift(File.expand_path("../../lib", __FILE__))
|
10
|
+
|
11
|
+
require "rspec/core"
|
12
|
+
require "inception/providers"
|
13
|
+
require "inception"
|
14
|
+
require "inception/cli"
|
15
|
+
|
16
|
+
# for the #sh helper
|
17
|
+
require "rake"
|
18
|
+
require "rake/file_utils"
|
19
|
+
|
20
|
+
# load all files in spec/support/* (but not lower down)
|
21
|
+
Dir[File.dirname(__FILE__) + '/support/*'].each do |path|
|
22
|
+
require path unless File.directory?(path)
|
23
|
+
end
|
24
|
+
|
25
|
+
def spec_asset(filename)
|
26
|
+
File.expand_path("../assets/#{filename}", __FILE__)
|
27
|
+
end
|
28
|
+
|
29
|
+
def setup_home_dir
|
30
|
+
home_dir = File.expand_path("../../tmp/home", __FILE__)
|
31
|
+
FileUtils.rm_rf(home_dir)
|
32
|
+
FileUtils.mkdir_p(home_dir)
|
33
|
+
ENV['HOME'] = home_dir
|
34
|
+
FileUtils.cp_r(spec_asset("gitconfig"), home_file(".gitconfig"))
|
35
|
+
end
|
36
|
+
|
37
|
+
# returns the file path to a file
|
38
|
+
# in the fake $HOME folder
|
39
|
+
def home_file(*path)
|
40
|
+
File.join(ENV['HOME'], *path)
|
41
|
+
end
|
42
|
+
|
43
|
+
RSpec.configure do |c|
|
44
|
+
c.before do
|
45
|
+
setup_home_dir
|
46
|
+
Fog::Mock.reset
|
47
|
+
end
|
48
|
+
|
49
|
+
c.color_enabled = true
|
50
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "active_support/core_ext/hash/keys"
|
2
|
+
module AwsHelpers
|
3
|
+
extend self
|
4
|
+
include SettingsHelper
|
5
|
+
|
6
|
+
def keep_after_test?
|
7
|
+
ENV['KEEP_AFTER_TEST']
|
8
|
+
end
|
9
|
+
|
10
|
+
def region
|
11
|
+
@region ||= "us-west-2"
|
12
|
+
end
|
13
|
+
|
14
|
+
def fog
|
15
|
+
@fog ||= Fog::Compute.new(fog_credentials.merge(:region => region))
|
16
|
+
end
|
17
|
+
|
18
|
+
def aws_credentials?
|
19
|
+
access_key = ENV['AWS_ACCESS_KEY_ID']
|
20
|
+
secret_key = ENV["AWS_SECRET_ACCESS_KEY"]
|
21
|
+
access_key && secret_key
|
22
|
+
end
|
23
|
+
|
24
|
+
def fog_credentials
|
25
|
+
@fog_credentials ||= begin
|
26
|
+
access_key = ENV['AWS_ACCESS_KEY_ID']
|
27
|
+
secret_key = ENV["AWS_SECRET_ACCESS_KEY"]
|
28
|
+
unless access_key && secret_key
|
29
|
+
raise "Please provided $AWS_ACCESS_KEY_ID and $AWS_SECRET_ACCESS_KEY"
|
30
|
+
end
|
31
|
+
credentials = {
|
32
|
+
:provider => 'AWS',
|
33
|
+
:aws_access_key_id => access_key,
|
34
|
+
:aws_secret_access_key => secret_key
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def prepare_aws(spec_name, aws_region, options={})
|
40
|
+
setup_home_dir
|
41
|
+
@cmd = nil
|
42
|
+
@fog = nil
|
43
|
+
create_manifest(options)
|
44
|
+
destroy_test_constructs
|
45
|
+
end
|
46
|
+
|
47
|
+
def unique_number
|
48
|
+
ENV['UNIQUE_NUMBER'] || Random.rand(100000)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_server_name
|
52
|
+
"test-inception"
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_manifest(options = {})
|
56
|
+
credentials = options.delete(:credentials) || fog_credentials
|
57
|
+
setting "provider.name", "aws"
|
58
|
+
setting "provider.credentials", credentials.stringify_keys
|
59
|
+
setting "provider.region", region
|
60
|
+
setting "inception.name", test_server_name
|
61
|
+
options.each { |key, value| setting(key, value) }
|
62
|
+
cmd.save_settings!
|
63
|
+
end
|
64
|
+
|
65
|
+
def destroy_test_constructs
|
66
|
+
puts "Destroying everything created by previous test... "
|
67
|
+
# destroy servers using inception-vm SG
|
68
|
+
provider.delete_servers_with_name(test_server_name)
|
69
|
+
provider.delete_volumes_with_name(test_server_name)
|
70
|
+
provider.delete_key_pair_if_exists(test_server_name)
|
71
|
+
provider.cleanup_unused_ip_addresses
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# assumes @cmd is Inception::Cli instance
|
2
|
+
module SettingsHelper
|
3
|
+
def cmd
|
4
|
+
@cmd ||= Inception::Cli.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def provider
|
8
|
+
cmd.provider_client
|
9
|
+
end
|
10
|
+
|
11
|
+
# Set a nested setting with "key1.key2.key3" notation
|
12
|
+
def setting(nested_key, value)
|
13
|
+
settings.set(nested_key, value)
|
14
|
+
end
|
15
|
+
|
16
|
+
# used by +SettingsSetter+ to access the settings
|
17
|
+
def settings
|
18
|
+
cmd.settings
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module StdoutCapture
|
2
|
+
# Captures stdout within the block
|
3
|
+
# Usage:
|
4
|
+
#
|
5
|
+
# out = capture_stdout do
|
6
|
+
# puts "this will not be shown"
|
7
|
+
# end
|
8
|
+
# out.should == "this will not be shown"
|
9
|
+
def capture_stdout(&block)
|
10
|
+
out = StringIO.new
|
11
|
+
$stdout = out
|
12
|
+
yield
|
13
|
+
return out
|
14
|
+
ensure
|
15
|
+
$stdout = STDOUT
|
16
|
+
end
|
17
|
+
end
|
data/spec/unit/.gitkeep
ADDED
File without changes
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path("../../support/aws/aws_helpers", __FILE__)
|
2
|
+
|
3
|
+
require "fog"
|
4
|
+
|
5
|
+
describe "AWS deployment deletion" do
|
6
|
+
include FileUtils
|
7
|
+
include StdoutCapture
|
8
|
+
include SettingsHelper
|
9
|
+
include AwsHelpers
|
10
|
+
|
11
|
+
before do
|
12
|
+
setup_home_dir
|
13
|
+
Fog.mock!
|
14
|
+
@cmd = Inception::Cli.new
|
15
|
+
setting "provider.name", "aws"
|
16
|
+
setting "provider.credentials.aws_access_key_id", "aws_access_key_id"
|
17
|
+
setting "provider.credentials.aws_secret_access_key", "aws_secret_access_key"
|
18
|
+
setting "provider.region", "us-west-2"
|
19
|
+
setting "next_deploy_actions.no_converge", true
|
20
|
+
capture_stdout { @cmd.deploy }
|
21
|
+
end
|
22
|
+
|
23
|
+
def perform_delete
|
24
|
+
config = { shell: Thor::Base.shell.new }
|
25
|
+
capture_stdout { @cmd.class.send(:dispatch, :delete, [], {:"non-interactive" => true}, config) }
|
26
|
+
end
|
27
|
+
|
28
|
+
it "clears out settings.yml" do
|
29
|
+
perform_delete
|
30
|
+
settings = ReadWriteSettings.new(File.expand_path("~/.inception_server/settings.yml"))
|
31
|
+
settings.exists?("provider").should_not be_nil
|
32
|
+
settings.exists?("git").should_not be_nil
|
33
|
+
settings.exists?("inception.provisioned.disk_device").should be_nil
|
34
|
+
settings.exists?("inception.provisioned.host").should be_nil
|
35
|
+
settings.exists?("inception.provisioned.ip_address").should be_nil
|
36
|
+
settings.exists?("inception.key_pair").should be_nil
|
37
|
+
settings.exists?("inception.provisioned.disk_device").should be_nil
|
38
|
+
end
|
39
|
+
end
|