blimpy 0.3.8 → 0.4.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/Gemfile +5 -0
- data/Rakefile +5 -1
- data/features/cli/start.feature +0 -22
- data/features/liveries.feature +9 -0
- data/features/openstack.feature +26 -0
- data/features/step_definitions/cli_steps.rb +2 -2
- data/lib/blimpy.rb +4 -0
- data/lib/blimpy/box.rb +58 -21
- data/lib/blimpy/boxes/aws.rb +9 -1
- data/lib/blimpy/boxes/openstack.rb +148 -2
- data/lib/blimpy/cli.rb +2 -2
- data/lib/blimpy/engine.rb +2 -1
- data/lib/blimpy/fleet.rb +10 -3
- data/lib/blimpy/keys.rb +3 -8
- data/lib/blimpy/version.rb +1 -1
- data/spec/blimpy/box_spec.rb +5 -3
- data/spec/blimpy/boxes/openstack_spec.rb +283 -0
- data/spec/blimpy/keys_spec.rb +3 -3
- metadata +14 -10
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -16,7 +16,11 @@ namespace :cucumber do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
Cucumber::Rake::Task.new('integration') do |t|
|
19
|
-
t.cucumber_opts = cucumber_opts + ' --tags @slow'
|
19
|
+
t.cucumber_opts = cucumber_opts + ' --tags @slow --tags ~@openstack'
|
20
|
+
end
|
21
|
+
|
22
|
+
Cucumber::Rake::Task.new('openstack') do |t|
|
23
|
+
t.cucumber_opts = cucumber_opts + ' --tags @openstack'
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
data/features/cli/start.feature
CHANGED
@@ -14,7 +14,6 @@ Feature: Start a VM or cluster of VMs in the cloud
|
|
14
14
|
"""
|
15
15
|
Blimpy.fleet do |f|
|
16
16
|
f.add(:aws) do |host|
|
17
|
-
host.group = 'Simple'
|
18
17
|
host.name = 'Cucumber Host'
|
19
18
|
end
|
20
19
|
end
|
@@ -46,7 +45,6 @@ Feature: Start a VM or cluster of VMs in the cloud
|
|
46
45
|
"""
|
47
46
|
Blimpy.fleet do |f|
|
48
47
|
f.add(:aws) do |host|
|
49
|
-
host.group = 'Simple'
|
50
48
|
host.name = 'Cucumber Host'
|
51
49
|
end
|
52
50
|
end
|
@@ -72,23 +70,3 @@ Feature: Start a VM or cluster of VMs in the cloud
|
|
72
70
|
When I run `blimpy start`
|
73
71
|
Then the exit status should be 0
|
74
72
|
|
75
|
-
@slow @destroy @openstack @wip
|
76
|
-
Scenario: start with an OpenStack Blimpfile
|
77
|
-
Given I have the Blimpfile:
|
78
|
-
"""
|
79
|
-
Blimpy.fleet do |f|
|
80
|
-
f.add(:openstack) do |host|
|
81
|
-
host.name = 'Cucumber Host'
|
82
|
-
end
|
83
|
-
end
|
84
|
-
"""
|
85
|
-
When I run `blimpy start`
|
86
|
-
Then the exit status should be 0
|
87
|
-
And the output should contain:
|
88
|
-
"""
|
89
|
-
Up, up and away!
|
90
|
-
"""
|
91
|
-
And the output should contain:
|
92
|
-
"""
|
93
|
-
online at:
|
94
|
-
"""
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Feature: Craft machines based on a livery
|
2
|
+
In order to bootstrap a machine that looks how I expect it to
|
3
|
+
As a Blimpy user
|
4
|
+
When I specify a specific livery then that livery should provision
|
5
|
+
the host the way I would expect it to.
|
6
|
+
|
7
|
+
|
8
|
+
@slow @destroy @wip
|
9
|
+
Scenario: freebsd_puppet
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Feature: Provision machines on an OpenStack cluster
|
2
|
+
In order to use a private cloud, powered by OpenStack
|
3
|
+
As a Blimpy user
|
4
|
+
I should be able to tspin up machines on OpenStack the same way I am able to
|
5
|
+
on AWS
|
6
|
+
|
7
|
+
|
8
|
+
@slow @destroy @openstack
|
9
|
+
Scenario: Start with a functional Blimpfile
|
10
|
+
Given I have the Blimpfile:
|
11
|
+
"""
|
12
|
+
Blimpy.fleet do |f|
|
13
|
+
f.add(:openstack) do |host|
|
14
|
+
host.name = 'Cucumber Host'
|
15
|
+
host.image_id = '5e624061-65cc-4e67-b6c5-8e7ac6e38ea7' # Maps to our intenral 'lucid-server' image
|
16
|
+
host.region = 'test' # This is the "test" tenant
|
17
|
+
host.flavor = 'm1.tiny'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
"""
|
21
|
+
When I run `blimpy start`
|
22
|
+
Then the exit status should be 0
|
23
|
+
And the output should contain:
|
24
|
+
"""
|
25
|
+
online at:
|
26
|
+
"""
|
@@ -21,8 +21,8 @@ Given /^I have a single VM running$/ do
|
|
21
21
|
@name = 'Cucumber host'
|
22
22
|
@server_id = '0xdeadbeef'
|
23
23
|
File.open(File.join(d, "#{@server_id}.blimp"), 'w') do |f|
|
24
|
-
f.write("name: #{@name}\n")
|
25
|
-
f.write("dns: foo.bar\n")
|
24
|
+
f.write(":name: #{@name}\n")
|
25
|
+
f.write(":dns: foo.bar\n")
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
data/lib/blimpy.rb
CHANGED
@@ -13,6 +13,8 @@ module Blimpy
|
|
13
13
|
fleet
|
14
14
|
end
|
15
15
|
|
16
|
+
class UnknownError < Exception
|
17
|
+
end
|
16
18
|
class InvalidBlimpFileError < Exception
|
17
19
|
end
|
18
20
|
class InvalidRegionError < Exception
|
@@ -23,4 +25,6 @@ module Blimpy
|
|
23
25
|
end
|
24
26
|
class InvalidShipException < Exception
|
25
27
|
end
|
28
|
+
class UnsupportedFeatureException < Exception
|
29
|
+
end
|
26
30
|
end
|
data/lib/blimpy/box.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'yaml'
|
1
3
|
require 'blimpy/helpers/state'
|
2
4
|
require 'blimpy/livery'
|
3
5
|
require 'blimpy/keys'
|
@@ -10,13 +12,14 @@ module Blimpy
|
|
10
12
|
|
11
13
|
attr_reader :allowed_regions, :region
|
12
14
|
attr_accessor :image_id, :flavor, :group, :ports
|
15
|
+
attr_accessor :dns, :internal_dns
|
13
16
|
attr_accessor :name, :tags, :fleet_id, :username, :livery
|
14
17
|
|
15
18
|
|
16
19
|
def self.from_instance_id(an_id, data)
|
17
|
-
return if data[
|
20
|
+
return if data[:type].nil?
|
18
21
|
|
19
|
-
name = data[
|
22
|
+
name = data[:type].to_sym
|
20
23
|
return unless Blimpy::Boxes.const_defined? name
|
21
24
|
|
22
25
|
klass = Blimpy::Boxes.const_get(name)
|
@@ -25,7 +28,7 @@ module Blimpy
|
|
25
28
|
return if server.nil?
|
26
29
|
|
27
30
|
box = klass.new(server)
|
28
|
-
box.
|
31
|
+
box.with_data(an_id, data)
|
29
32
|
box
|
30
33
|
end
|
31
34
|
|
@@ -49,22 +52,27 @@ module Blimpy
|
|
49
52
|
end
|
50
53
|
|
51
54
|
def online!
|
52
|
-
|
53
|
-
f.write("dns: #{@server.dns_name}\n")
|
54
|
-
f.write("internal_dns: #{@server.private_dns_name}\n")
|
55
|
-
end
|
55
|
+
write_state_file
|
56
56
|
end
|
57
57
|
|
58
58
|
def validate!
|
59
59
|
raise NotImplementedError, '#validate! should be defined in a subclass of Blimpy::Box'
|
60
60
|
end
|
61
61
|
|
62
|
+
def prestart
|
63
|
+
end
|
64
|
+
|
62
65
|
def start
|
63
66
|
ensure_state_folder
|
67
|
+
prestart
|
64
68
|
@server = create_host
|
69
|
+
poststart
|
65
70
|
write_state_file
|
66
71
|
end
|
67
72
|
|
73
|
+
def poststart
|
74
|
+
end
|
75
|
+
|
68
76
|
def bootstrap
|
69
77
|
@exec_commands = false
|
70
78
|
unless livery.nil?
|
@@ -85,20 +93,42 @@ module Blimpy
|
|
85
93
|
end
|
86
94
|
end
|
87
95
|
|
96
|
+
def predestroy
|
97
|
+
end
|
98
|
+
|
88
99
|
def destroy
|
89
100
|
unless @server.nil?
|
101
|
+
predestroy
|
90
102
|
@server.destroy
|
103
|
+
postdestroy
|
91
104
|
File.unlink(state_file)
|
92
105
|
end
|
93
106
|
end
|
94
107
|
|
108
|
+
def postdestroy
|
109
|
+
end
|
110
|
+
|
111
|
+
def type
|
112
|
+
# We only really care about the class name as part of the Blimpy::Boxes
|
113
|
+
# module
|
114
|
+
self.class.to_s.split('::').last
|
115
|
+
end
|
116
|
+
|
117
|
+
def serializable_attributes
|
118
|
+
[:type, :name, :region, :dns, :internal_dns]
|
119
|
+
end
|
120
|
+
|
121
|
+
def immutable_attributes
|
122
|
+
[:type]
|
123
|
+
end
|
124
|
+
|
95
125
|
def write_state_file
|
126
|
+
data = {}
|
127
|
+
serializable_attributes.each do |attr|
|
128
|
+
data[attr] = self.send(attr)
|
129
|
+
end
|
96
130
|
File.open(state_file, 'w') do |f|
|
97
|
-
|
98
|
-
# module
|
99
|
-
f.write("type: #{self.class.to_s.split('::').last}\n")
|
100
|
-
f.write("name: #{@name}\n")
|
101
|
-
f.write("region: #{@region}\n")
|
131
|
+
f.write(data.to_yaml)
|
102
132
|
end
|
103
133
|
end
|
104
134
|
|
@@ -122,11 +152,13 @@ module Blimpy
|
|
122
152
|
|
123
153
|
|
124
154
|
def with_data(ship_id, data)
|
125
|
-
|
126
|
-
|
155
|
+
data.each do |key, value|
|
156
|
+
next if immutable_attributes.include? key.to_sym
|
157
|
+
self.send("#{key}=", value)
|
158
|
+
end
|
127
159
|
end
|
128
160
|
|
129
|
-
def
|
161
|
+
def dns
|
130
162
|
@dns ||= begin
|
131
163
|
if @server.nil?
|
132
164
|
'no name'
|
@@ -136,9 +168,14 @@ module Blimpy
|
|
136
168
|
end
|
137
169
|
end
|
138
170
|
|
139
|
-
def
|
140
|
-
|
141
|
-
|
171
|
+
def internal_dns
|
172
|
+
@internal_dns ||= begin
|
173
|
+
if @server.nil?
|
174
|
+
'no name'
|
175
|
+
else
|
176
|
+
@server.private_dns_name
|
177
|
+
end
|
178
|
+
end
|
142
179
|
end
|
143
180
|
|
144
181
|
def run_command(*args)
|
@@ -156,13 +193,13 @@ module Blimpy
|
|
156
193
|
args = ARGV[2 .. -1]
|
157
194
|
end
|
158
195
|
run_command('ssh', '-o', 'StrictHostKeyChecking=no',
|
159
|
-
'-l', username,
|
196
|
+
'-l', username, dns, *args)
|
160
197
|
end
|
161
198
|
|
162
199
|
def scp_file(filename, directory='')
|
163
200
|
filename = File.expand_path(filename)
|
164
201
|
run_command('scp', '-o', 'StrictHostKeyChecking=no',
|
165
|
-
filename, "#{username}@#{
|
202
|
+
filename, "#{username}@#{dns}:#{directory}", *ARGV[3..-1])
|
166
203
|
end
|
167
204
|
|
168
205
|
def bootstrap_livery
|
@@ -187,7 +224,7 @@ module Blimpy
|
|
187
224
|
'--exclude=.svn',
|
188
225
|
'--exclude=.blimpy.d',
|
189
226
|
'.',
|
190
|
-
"#{username}@#{
|
227
|
+
"#{username}@#{dns}:#{dir_name}/")
|
191
228
|
else
|
192
229
|
puts "Remote host has no rsync(1), falling back to copying a full tarball over"
|
193
230
|
tarball = Blimpy::Livery.tarball_directory(Dir.pwd)
|
data/lib/blimpy/boxes/aws.rb
CHANGED
@@ -42,10 +42,18 @@ module Blimpy::Boxes
|
|
42
42
|
|
43
43
|
private
|
44
44
|
|
45
|
+
def import_key
|
46
|
+
material = Blimpy::Keys.public_key
|
47
|
+
begin
|
48
|
+
fog.import_key_pair(Blimpy::Keys.key_name, material)
|
49
|
+
rescue Fog::Compute::AWS::Error => e
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
45
53
|
def create_host
|
46
54
|
tags = @tags.merge({:Name => @name, :CreatedBy => 'Blimpy', :BlimpyFleetId => @fleet_id})
|
47
55
|
|
48
|
-
|
56
|
+
import_key
|
49
57
|
generated_group = Blimpy::SecurityGroups.ensure_group(fog, @ports + [22])
|
50
58
|
groups = [@group, generated_group].compact
|
51
59
|
fog.servers.create(:image_id => @image_id,
|
@@ -4,9 +4,155 @@ require 'blimpy/boxes'
|
|
4
4
|
module Blimpy::Boxes
|
5
5
|
class OpenStack < Blimpy::Box
|
6
6
|
def self.fog_server_for_instance(id, blimpdata)
|
7
|
-
region = blimpdata[
|
8
|
-
fog = Fog::Compute.new(:provider => 'OpenStack', :
|
7
|
+
region = blimpdata[:region]
|
8
|
+
fog = Fog::Compute.new(:provider => 'OpenStack', :openstack_tenant => region)
|
9
9
|
fog.servers.get(id)
|
10
10
|
end
|
11
|
+
|
12
|
+
class FloatingIp
|
13
|
+
attr_accessor :address, :id
|
14
|
+
|
15
|
+
def initialize(address, id)
|
16
|
+
@address = address
|
17
|
+
@id = id
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_yaml(*args)
|
21
|
+
{:address => address, :id => id}.to_yaml
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor :key_name, :floating_ip
|
26
|
+
|
27
|
+
def initialize(server=nil)
|
28
|
+
super(server)
|
29
|
+
@username = 'ubuntu'
|
30
|
+
@flavor = 'm1.tiny'
|
31
|
+
@group = 'default'
|
32
|
+
@key_name = nil
|
33
|
+
@floating_ip = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def ports=(new_pors)
|
37
|
+
raise Blimpy::UnsupportedFeatureException, 'Opening arbitrary ports in OpenStack is currently not supported'
|
38
|
+
end
|
39
|
+
|
40
|
+
def wait_for_state(until_state, &block)
|
41
|
+
until @server.ready?
|
42
|
+
sleep 1
|
43
|
+
@server.reload
|
44
|
+
end
|
45
|
+
# OpenStack doesn't seem to like it if you try to associate the IP
|
46
|
+
# address too early, but will properly associate it after the machine is
|
47
|
+
# ready
|
48
|
+
associate_ip
|
49
|
+
end
|
50
|
+
|
51
|
+
def serializable_attributes
|
52
|
+
super + [:floating_ip]
|
53
|
+
end
|
54
|
+
|
55
|
+
def dns
|
56
|
+
if floating_ip.nil?
|
57
|
+
'unavailable'
|
58
|
+
else
|
59
|
+
floating_ip.address
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def internal_dns
|
64
|
+
'unavailable'
|
65
|
+
end
|
66
|
+
|
67
|
+
def validate!
|
68
|
+
if @region.nil?
|
69
|
+
raise Blimpy::BoxValidationError, "Cannot spin up machine without a set region"
|
70
|
+
end
|
71
|
+
|
72
|
+
if flavor_id(@flavor).nil?
|
73
|
+
raise Blimpy::BoxValidationError, "'#{@flavor}' is not a valid OpenStack tenant name"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def fog
|
78
|
+
@fog ||= begin
|
79
|
+
Fog::Compute.new(:provider => 'openstack', :openstack_tenant => @region)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def flavors
|
84
|
+
@flavors ||= fog.flavors
|
85
|
+
end
|
86
|
+
|
87
|
+
def flavor_id(name)
|
88
|
+
flavors.each do |flavor|
|
89
|
+
return flavor.id if flavor.name == name
|
90
|
+
end
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def prestart
|
95
|
+
allocate_ip
|
96
|
+
end
|
97
|
+
|
98
|
+
def allocate_ip
|
99
|
+
response = fog.allocate_address
|
100
|
+
unless response.status == 200
|
101
|
+
raise Blimpy::UnknownError, "Blimpy was unable to allocate a floating IP address; #{response.inspect}"
|
102
|
+
end
|
103
|
+
|
104
|
+
details = response.body['floating_ip']
|
105
|
+
@floating_ip = FloatingIp.new(details['ip'], details['id'])
|
106
|
+
end
|
107
|
+
|
108
|
+
def associate_ip
|
109
|
+
if floating_ip.nil?
|
110
|
+
raise Blimpy::UnknownError, "Blimpy cannot associate a floating IP until it's been allocated properly!"
|
111
|
+
end
|
112
|
+
response = fog.associate_address(@server.id, floating_ip.address)
|
113
|
+
|
114
|
+
unless response.status == 202
|
115
|
+
raise Blimpy::UnknownError, "Blimpy failed to associate the IP somehow #{response.inspect}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def predestroy
|
120
|
+
disassociate_ip unless floating_ip.nil?
|
121
|
+
end
|
122
|
+
|
123
|
+
def postdestroy
|
124
|
+
deallocate_ip unless floating_ip.nil?
|
125
|
+
end
|
126
|
+
|
127
|
+
def disassociate_ip
|
128
|
+
fog.disassociate_address(@server.id, floating_ip.address)
|
129
|
+
end
|
130
|
+
|
131
|
+
def deallocate_ip
|
132
|
+
fog.release_address(floating_ip.id)
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def import_key
|
138
|
+
material = Blimpy::Keys.public_key
|
139
|
+
begin
|
140
|
+
fog.create_key_pair(Blimpy::Keys.key_name, material)
|
141
|
+
rescue Excon::Errors::Conflict => e
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def create_host
|
146
|
+
tags = @tags.merge({:Name => @name, :CreatedBy => 'Blimpy', :BlimpyFleetId => @fleet_id})
|
147
|
+
|
148
|
+
groups = [@group]
|
149
|
+
import_key
|
150
|
+
fog.servers.create(:image_ref => image_id,
|
151
|
+
:flavor_ref => flavor_id(@flavor),
|
152
|
+
:key_name => Blimpy::Keys.key_name,
|
153
|
+
:groups => groups,
|
154
|
+
:name => @name,
|
155
|
+
:tags => tags)
|
156
|
+
end
|
11
157
|
end
|
12
158
|
end
|
data/lib/blimpy/cli.rb
CHANGED
@@ -28,7 +28,7 @@ module Blimpy
|
|
28
28
|
ship_id = nil
|
29
29
|
data = nil
|
30
30
|
engine.fleet.members.each do |instance_id, instance_data|
|
31
|
-
next unless instance_data[
|
31
|
+
next unless instance_data[:name] == name
|
32
32
|
ship_id = instance_id
|
33
33
|
data = instance_data
|
34
34
|
break
|
@@ -88,7 +88,7 @@ module Blimpy
|
|
88
88
|
blimps.each do |blimp, data|
|
89
89
|
instance_id = File.basename(blimp)
|
90
90
|
instance_id = instance_id.split('.blimp').first
|
91
|
-
puts "#{data[
|
91
|
+
puts "#{data[:name]} (#{instance_id}) is: online at #{data[:dns]}"
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
data/lib/blimpy/engine.rb
CHANGED
data/lib/blimpy/fleet.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'blimpy/helpers/state'
|
2
2
|
require 'blimpy/boxes/aws'
|
3
|
+
require 'blimpy/boxes/openstack'
|
3
4
|
|
4
5
|
module Blimpy
|
5
6
|
class Fleet
|
@@ -14,7 +15,7 @@ module Blimpy
|
|
14
15
|
end
|
15
16
|
|
16
17
|
def valid_types
|
17
|
-
[:aws]
|
18
|
+
[:aws, :openstack]
|
18
19
|
end
|
19
20
|
|
20
21
|
def add(box_type, &block)
|
@@ -25,7 +26,13 @@ module Blimpy
|
|
25
26
|
return false
|
26
27
|
end
|
27
28
|
|
28
|
-
box =
|
29
|
+
box = nil
|
30
|
+
if box_type == :aws
|
31
|
+
box = Blimpy::Boxes::AWS.new
|
32
|
+
end
|
33
|
+
if box_type == :openstack
|
34
|
+
box = Blimpy::Boxes::OpenStack.new
|
35
|
+
end
|
29
36
|
|
30
37
|
if box.nil?
|
31
38
|
return false
|
@@ -115,7 +122,7 @@ module Blimpy
|
|
115
122
|
host.wait_for_state('running') { }
|
116
123
|
@airborn = true
|
117
124
|
print "\n"
|
118
|
-
puts ">> #{host.name} online at: #{host.
|
125
|
+
puts ">> #{host.name} online at: #{host.dns}"
|
119
126
|
host.online!
|
120
127
|
host.bootstrap
|
121
128
|
puts
|
data/lib/blimpy/keys.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
require 'fog'
|
2
1
|
require 'socket'
|
3
2
|
|
4
3
|
module Blimpy
|
5
4
|
module Keys
|
6
|
-
def self.
|
5
|
+
def self.public_key
|
7
6
|
filename = File.expand_path('~/.ssh/id_rsa.pub')
|
8
7
|
unless File.exists? filename
|
9
8
|
filename = File.expand_path('~/.ssh/id_dsa.pub')
|
@@ -12,15 +11,11 @@ module Blimpy
|
|
12
11
|
end
|
13
12
|
end
|
14
13
|
|
15
|
-
|
16
|
-
begin
|
17
|
-
fog.import_key_pair(key_name, material)
|
18
|
-
rescue Fog::Compute::AWS::Error => e
|
19
|
-
end
|
14
|
+
File.open(filename, 'r').read
|
20
15
|
end
|
21
16
|
|
22
17
|
def self.key_name
|
23
|
-
"Blimpy-#{ENV['USER']}
|
18
|
+
"Blimpy-#{ENV['USER']}-#{Socket.gethostname}"
|
24
19
|
end
|
25
20
|
end
|
26
21
|
end
|
data/lib/blimpy/version.rb
CHANGED
data/spec/blimpy/box_spec.rb
CHANGED
@@ -61,7 +61,7 @@ describe Blimpy::Box do
|
|
61
61
|
end
|
62
62
|
|
63
63
|
it 'should set the dns_name' do
|
64
|
-
subject.
|
64
|
+
subject.dns.should == data['dns']
|
65
65
|
end
|
66
66
|
|
67
67
|
it 'should set the region' do
|
@@ -75,6 +75,8 @@ describe Blimpy::Box do
|
|
75
75
|
let(:server) do
|
76
76
|
server = double('Fog::Compute::AWS::Server')
|
77
77
|
server.stub(:id).and_return(server_id)
|
78
|
+
server.stub(:dns_name).and_return('test')
|
79
|
+
server.stub(:private_dns_name).and_return('test')
|
78
80
|
server
|
79
81
|
end
|
80
82
|
|
@@ -126,7 +128,7 @@ describe Blimpy::Box do
|
|
126
128
|
end
|
127
129
|
|
128
130
|
it 'should fail if the "type" is not a defined Box class' do
|
129
|
-
result = Blimpy::Box.from_instance_id('someid', {
|
131
|
+
result = Blimpy::Box.from_instance_id('someid', {:type => 'MAGIC'})
|
130
132
|
result.should be_nil
|
131
133
|
end
|
132
134
|
|
@@ -137,7 +139,7 @@ describe Blimpy::Box do
|
|
137
139
|
end
|
138
140
|
|
139
141
|
it 'should create a new AWS Box instance' do
|
140
|
-
result = Blimpy::Box.from_instance_id('someid', {
|
142
|
+
result = Blimpy::Box.from_instance_id('someid', {:type => 'AWS'})
|
141
143
|
result.should be_instance_of Blimpy::Boxes::AWS
|
142
144
|
end
|
143
145
|
end
|
@@ -1,6 +1,18 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'blimpy/boxes/openstack'
|
3
3
|
|
4
|
+
shared_context 'valid floating_ip' do
|
5
|
+
let(:floating) do
|
6
|
+
floating = double('FloatingIp')
|
7
|
+
floating.stub(:address).and_return('127.0.0.1')
|
8
|
+
floating.stub(:id).and_return(7)
|
9
|
+
floating
|
10
|
+
end
|
11
|
+
before :each do
|
12
|
+
subject.stub(:floating_ip).and_return(floating)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
4
16
|
describe Blimpy::Boxes::OpenStack do
|
5
17
|
describe '#image_id' do
|
6
18
|
it 'should be nil by default' do
|
@@ -8,6 +20,24 @@ describe Blimpy::Boxes::OpenStack do
|
|
8
20
|
end
|
9
21
|
end
|
10
22
|
|
23
|
+
describe '#username' do
|
24
|
+
it 'should be "ubuntu" by default' do
|
25
|
+
subject.username.should == 'ubuntu'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#flavor' do
|
30
|
+
it 'should be "m1.tiny" by default' do
|
31
|
+
subject.flavor.should == 'm1.tiny'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#group' do
|
36
|
+
it 'should be "default" by default' do
|
37
|
+
subject.group.should == 'default'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
11
41
|
describe '#allowed_regions' do
|
12
42
|
it 'should be nil by default' do
|
13
43
|
subject.allowed_regions.should be_nil
|
@@ -21,4 +51,257 @@ describe Blimpy::Boxes::OpenStack do
|
|
21
51
|
subject.region.should == :elbonia
|
22
52
|
end
|
23
53
|
end
|
54
|
+
|
55
|
+
describe '#ports=' do
|
56
|
+
it 'should be disabled currently' do
|
57
|
+
expect {
|
58
|
+
subject.ports = [22, 8080]
|
59
|
+
}.to raise_error(Blimpy::UnsupportedFeatureException)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#validate!' do
|
64
|
+
it 'should raise a validation error if there isn\'t a region' do
|
65
|
+
subject.region = nil
|
66
|
+
expect {
|
67
|
+
subject.validate!
|
68
|
+
}.to raise_error(Blimpy::BoxValidationError)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with mocked flavors' do
|
73
|
+
let(:fog) { double('Fog') }
|
74
|
+
let(:flavors) do
|
75
|
+
flavor = double('Fog::Compute::OpenStack::Flavor')
|
76
|
+
flavor.stub(:id).and_return('1')
|
77
|
+
flavor.stub(:name).and_return('m1.tiny')
|
78
|
+
[flavor]
|
79
|
+
end
|
80
|
+
|
81
|
+
before :each do
|
82
|
+
fog.should_receive(:flavors).and_return(flavors)
|
83
|
+
subject.should_receive(:fog).and_return(fog)
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#flavors' do
|
87
|
+
it 'should pull the list of flavors from Fog' do
|
88
|
+
subject.flavors.should == flavors
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#flavor_id' do
|
93
|
+
it 'should filter out the right flavor' do
|
94
|
+
subject.flavor_id('m1.tiny').should == '1'
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should return nil if the flavor doesn\'t exist' do
|
98
|
+
subject.flavor_id('invalid').should be_nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '#validate!' do
|
103
|
+
it 'should raise a validation error with an invalid flavor' do
|
104
|
+
subject.flavor = 'invalid'
|
105
|
+
subject.region = 'test'
|
106
|
+
expect {
|
107
|
+
subject.validate!
|
108
|
+
}.to raise_error(Blimpy::BoxValidationError)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
describe '#prestart' do
|
116
|
+
it 'should allocate an IP' do
|
117
|
+
subject.should_receive(:allocate_ip)
|
118
|
+
subject.prestart
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe '#allocate_ip' do
|
123
|
+
let(:fog) { double('Fog') }
|
124
|
+
|
125
|
+
before :each do
|
126
|
+
subject.stub(:fog).and_return(fog)
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'with a bad response' do
|
130
|
+
let(:response) do
|
131
|
+
response = double('Excon::Response')
|
132
|
+
response.stub(:status).and_return(500)
|
133
|
+
response
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'should raise an error if we cannot allocate the IP' do
|
137
|
+
fog.should_receive(:allocate_address).and_return(response)
|
138
|
+
expect {
|
139
|
+
subject.allocate_ip
|
140
|
+
}.to raise_error(Blimpy::UnknownError)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context 'with a good response' do
|
145
|
+
let(:response) do
|
146
|
+
response = double('Excon::Response')
|
147
|
+
response.stub(:status).and_return(200)
|
148
|
+
response.stub(:body).and_return({"floating_ip"=>
|
149
|
+
{"instance_id"=>nil,
|
150
|
+
"ip"=>"10.38.12.109",
|
151
|
+
"fixed_ip"=>nil,
|
152
|
+
"id"=>109,
|
153
|
+
"pool"=>"nova"}})
|
154
|
+
response
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should allocate and store the floating IP info' do
|
158
|
+
fog.should_receive(:allocate_address).and_return(response)
|
159
|
+
|
160
|
+
subject.allocate_ip
|
161
|
+
subject.floating_ip.address.should == '10.38.12.109'
|
162
|
+
subject.floating_ip.id.should == 109
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '#associate_ip' do
|
168
|
+
let(:fog) { double('Fog') }
|
169
|
+
include_context 'valid floating_ip'
|
170
|
+
let(:server) do
|
171
|
+
server = double('Fog::Compute::Server')
|
172
|
+
server.stub(:id).and_return(server_id)
|
173
|
+
server
|
174
|
+
end
|
175
|
+
let(:server_id) { 'fake-id' }
|
176
|
+
|
177
|
+
subject { described_class.new(server) }
|
178
|
+
|
179
|
+
before :each do
|
180
|
+
subject.stub(:fog).and_return(fog)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'should raise an exception if a floating IP hasn\'t been created' do
|
184
|
+
subject.stub(:floating_ip).and_return(nil)
|
185
|
+
expect {
|
186
|
+
subject.associate_ip
|
187
|
+
}.to raise_error(Blimpy::UnknownError)
|
188
|
+
end
|
189
|
+
|
190
|
+
context 'with a good response' do
|
191
|
+
let(:response) do
|
192
|
+
response = double('Excon::Response')
|
193
|
+
response.stub(:status).and_return(202)
|
194
|
+
response
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'should associate the right IP to the right instance ID' do
|
198
|
+
fog.should_receive(:associate_address).with(server_id, floating.address).and_return(response)
|
199
|
+
subject.associate_ip
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
context 'with a bad response' do
|
204
|
+
let(:response) do
|
205
|
+
response = double('Excon::Response')
|
206
|
+
response.stub(:status).and_return(500)
|
207
|
+
response
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'should raise an error' do
|
211
|
+
subject.stub(:floating_ip).and_return(floating)
|
212
|
+
fog.should_receive(:associate_address).with(server_id, floating.address).and_return(response)
|
213
|
+
expect {
|
214
|
+
subject.associate_ip
|
215
|
+
}.to raise_error(Blimpy::UnknownError)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
describe '#predestroy' do
|
221
|
+
context 'if the server has a floating IP' do
|
222
|
+
include_context 'valid floating_ip'
|
223
|
+
|
224
|
+
it 'should disasscoaite it' do
|
225
|
+
subject.should_receive(:disassociate_ip)
|
226
|
+
subject.predestroy
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
context 'if the server has no floating IP' do
|
231
|
+
it 'should not try to disassociate it' do
|
232
|
+
subject.should_receive(:disassociate_ip).never
|
233
|
+
subject.predestroy
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
describe '#postdestroy' do
|
239
|
+
context 'if the server has a floating IP' do
|
240
|
+
include_context 'valid floating_ip'
|
241
|
+
|
242
|
+
it 'should deallocate the IP' do
|
243
|
+
subject.should_receive(:deallocate_ip)
|
244
|
+
subject.postdestroy
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
context 'if the server has no floating IP' do
|
249
|
+
it 'should not try to deallocate the IP' do
|
250
|
+
subject.should_receive(:deallocate_ip).never
|
251
|
+
subject.postdestroy
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
describe '#disassociate_ip' do
|
257
|
+
let(:fog) { double('Fog') }
|
258
|
+
include_context 'valid floating_ip'
|
259
|
+
let(:server) do
|
260
|
+
server = double('Fog::Compute::Server')
|
261
|
+
server.stub(:id).and_return(server_id)
|
262
|
+
server
|
263
|
+
end
|
264
|
+
let(:server_id) { 'fake-id' }
|
265
|
+
|
266
|
+
subject { described_class.new(server) }
|
267
|
+
|
268
|
+
before :each do
|
269
|
+
subject.stub(:fog).and_return(fog)
|
270
|
+
end
|
271
|
+
|
272
|
+
it 'should disassociate the right IP to the right instance ID' do
|
273
|
+
fog.should_receive(:disassociate_address).with(server_id, floating.address)
|
274
|
+
subject.disassociate_ip
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
describe '#deallocate_ip' do
|
279
|
+
let(:fog) { double('Fog') }
|
280
|
+
include_context 'valid floating_ip'
|
281
|
+
|
282
|
+
before :each do
|
283
|
+
subject.stub(:fog).and_return(fog)
|
284
|
+
end
|
285
|
+
|
286
|
+
context 'with a good response' do
|
287
|
+
let(:response) do
|
288
|
+
response = double('Excon::Response')
|
289
|
+
response.stub(:status).and_return(202)
|
290
|
+
response
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'should release the right IP by floating_ip ID' do
|
294
|
+
fog.should_receive(:release_address).with(floating.id).and_return(response)
|
295
|
+
subject.deallocate_ip
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
describe Blimpy::Boxes::OpenStack::FloatingIp do
|
302
|
+
subject do
|
303
|
+
described_class.new('127.0.0.1', 1)
|
304
|
+
end
|
305
|
+
|
306
|
+
it { should respond_to(:to_yaml) }
|
24
307
|
end
|
data/spec/blimpy/keys_spec.rb
CHANGED
@@ -2,12 +2,12 @@ require 'spec_helper'
|
|
2
2
|
require 'blimpy/keys'
|
3
3
|
|
4
4
|
describe Blimpy::Keys do
|
5
|
-
describe '#
|
5
|
+
describe '#public_key' do
|
6
6
|
context 'with no SSH keys' do
|
7
7
|
it 'should raise a SSHKeyNotFoundError' do
|
8
8
|
File.stub(:exists?).and_return(false)
|
9
9
|
expect {
|
10
|
-
subject.
|
10
|
+
subject.public_key
|
11
11
|
}.to raise_error(Blimpy::SSHKeyNotFoundError)
|
12
12
|
end
|
13
13
|
end
|
@@ -21,7 +21,7 @@ describe Blimpy::Keys do
|
|
21
21
|
it do
|
22
22
|
hostname = 'rspec'
|
23
23
|
Socket.should_receive(:gethostname).and_return(hostname)
|
24
|
-
subject.key_name.should == "Blimpy-tester
|
24
|
+
subject.key_name.should == "Blimpy-tester-#{hostname}"
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blimpy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-20 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: fog
|
16
|
-
requirement: &
|
16
|
+
requirement: &23292620 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *23292620
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: thor
|
27
|
-
requirement: &
|
27
|
+
requirement: &23292160 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *23292160
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: minitar
|
38
|
-
requirement: &
|
38
|
+
requirement: &23291720 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *23291720
|
47
47
|
description: Blimpy is a tool for managing a fleet of machines in the CLOUD!
|
48
48
|
email:
|
49
49
|
- tyler@monkeypox.org
|
@@ -65,6 +65,8 @@ files:
|
|
65
65
|
- features/cli/ssh.feature
|
66
66
|
- features/cli/start.feature
|
67
67
|
- features/cli/status.feature
|
68
|
+
- features/liveries.feature
|
69
|
+
- features/openstack.feature
|
68
70
|
- features/step_definitions/cli_steps.rb
|
69
71
|
- features/support/env.rb
|
70
72
|
- features/support/hooks.rb
|
@@ -108,7 +110,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
108
110
|
version: '0'
|
109
111
|
segments:
|
110
112
|
- 0
|
111
|
-
hash: -
|
113
|
+
hash: -3073527661709686604
|
112
114
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
115
|
none: false
|
114
116
|
requirements:
|
@@ -117,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
119
|
version: '0'
|
118
120
|
segments:
|
119
121
|
- 0
|
120
|
-
hash: -
|
122
|
+
hash: -3073527661709686604
|
121
123
|
requirements: []
|
122
124
|
rubyforge_project:
|
123
125
|
rubygems_version: 1.8.10
|
@@ -131,6 +133,8 @@ test_files:
|
|
131
133
|
- features/cli/ssh.feature
|
132
134
|
- features/cli/start.feature
|
133
135
|
- features/cli/status.feature
|
136
|
+
- features/liveries.feature
|
137
|
+
- features/openstack.feature
|
134
138
|
- features/step_definitions/cli_steps.rb
|
135
139
|
- features/support/env.rb
|
136
140
|
- features/support/hooks.rb
|