kitchen-openstack 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/.travis.yml +0 -1
- data/CHANGELOG.md +26 -0
- data/README.md +11 -3
- data/Rakefile +8 -2
- data/kitchen-openstack.gemspec +3 -1
- data/lib/kitchen/driver/openstack.rb +63 -37
- data/lib/kitchen/driver/openstack_version.rb +1 -1
- data/spec/kitchen/driver/openstack_spec.rb +404 -0
- data/spec/spec_helper.rb +22 -0
- metadata +44 -8
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# 0.2.0 / 2013-05-11
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
* PR [#7][] - `disable_ssl_validation` wasn't being respected on destroy
|
|
6
|
+
|
|
7
|
+
### New Features
|
|
8
|
+
|
|
9
|
+
* PR [#10][] - Support optional `openstack_region` and `openstack_service_name`
|
|
10
|
+
* PR [#2][] - Support `key_name:` option; via [@stevendanna][]
|
|
11
|
+
|
|
12
|
+
### Improvements
|
|
13
|
+
|
|
14
|
+
* PR [#7][] - Clean up/refactor to pass style checks
|
|
15
|
+
* PR [#9][] - Add some (probably overkill) RSpec tests
|
|
16
|
+
|
|
17
|
+
# 0.1.0 / 2013-03-12
|
|
18
|
+
|
|
19
|
+
* Initial release! Woo!
|
|
20
|
+
|
|
21
|
+
[#10]: https://github.com/RoboticCheese/kitchen-openstack/pull/10
|
|
22
|
+
[#9]: https://github.com/RoboticCheese/kitchen-openstack/pull/9
|
|
23
|
+
[#7]: https://github.com/RoboticCheese/kitchen-openstack/pull/7
|
|
24
|
+
[#2]: https://github.com/RoboticCheese/kitchen-openstack/pull/2
|
|
25
|
+
|
|
26
|
+
[@stevendanna]: https://github.com/stevendanna
|
data/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
[](https://travis-ci.org/RoboticCheese/kitchen-openstack) [](https://codeclimate.com/github/RoboticCheese/kitchen-openstack)
|
|
2
|
+
|
|
1
3
|
# Kitchen::OpenStack
|
|
2
4
|
|
|
3
5
|
An OpenStack Nova driver for Test Kitchen 1.0!
|
|
@@ -28,7 +30,6 @@ Provide, at a minimum, the required driver options in your `.kitchen.yml` file:
|
|
|
28
30
|
openstack_username: [YOUR OPENSTACK USERNAME]
|
|
29
31
|
openstack_api_key: [YOUR OPENSTACK API KEY]
|
|
30
32
|
openstack_auth_url: [YOUR OPENSTACK AUTH URL]
|
|
31
|
-
openstack_tenant: [YOUR OPENSTACK TENANT ID]
|
|
32
33
|
require_chef_omnibus: latest (if you'll be using Chef)
|
|
33
34
|
image_ref: [SERVER IMAGE ID]
|
|
34
35
|
flavor_ref: [SERVER FLAVOR ID]
|
|
@@ -41,12 +42,19 @@ options:
|
|
|
41
42
|
public_key_path: [PATH TO YOUR SSH PUBLIC KEY]
|
|
42
43
|
username: [SSH USER]
|
|
43
44
|
port: [SSH PORT]
|
|
45
|
+
key_name: [SSH KEY NAME]
|
|
46
|
+
openstack_tenant: [YOUR OPENSTACK TENANT ID]
|
|
47
|
+
openstack_region: [A VALID OPENSTACK REGION]
|
|
48
|
+
openstack_service_name: [YOUR OPENSTACK COMPUTE SERVICE NAME]
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
If a key\_name is provided it will be used instead of any
|
|
51
|
+
public\_key\_path that is specified.
|
|
47
52
|
|
|
48
53
|
disable_ssl_validation: true
|
|
49
54
|
|
|
55
|
+
Only disable SSL cert validation if you absolutely know what you are doing,
|
|
56
|
+
but are stuck with an OpenStack deployment without valid SSL certs.
|
|
57
|
+
|
|
50
58
|
## Contributing
|
|
51
59
|
|
|
52
60
|
1. Fork it
|
data/Rakefile
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
require 'bundler/gem_tasks'
|
|
2
2
|
require 'tailor/rake_task'
|
|
3
3
|
require 'cane/rake_task'
|
|
4
|
+
require 'rspec/core/rake_task'
|
|
4
5
|
|
|
5
6
|
desc 'Run Cane to check quality metrics'
|
|
6
7
|
Cane::RakeTask.new
|
|
7
8
|
|
|
8
9
|
desc 'Run Tailor to lint check code'
|
|
9
|
-
Tailor::RakeTask.new
|
|
10
|
+
Tailor::RakeTask.new do |task|
|
|
11
|
+
task.file_set '**/**/*.rb'
|
|
12
|
+
end
|
|
10
13
|
|
|
11
14
|
desc 'Display LOC stats'
|
|
12
15
|
task :loc do
|
|
@@ -14,6 +17,9 @@ task :loc do
|
|
|
14
17
|
sh 'countloc -r lib/kitchen'
|
|
15
18
|
end
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
desc 'Run RSpec unit tests'
|
|
21
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
22
|
+
|
|
23
|
+
task :default => [ :cane, :tailor, :loc, :spec ]
|
|
18
24
|
|
|
19
25
|
# vim: ai et ts=2 sts=2 sw=2 ft=ruby fdm=marker
|
data/kitchen-openstack.gemspec
CHANGED
|
@@ -19,12 +19,14 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
spec.require_paths = ['lib']
|
|
20
20
|
|
|
21
21
|
spec.add_dependency 'test-kitchen', '~> 1.0.0.alpha'
|
|
22
|
+
spec.add_dependency 'fog', '~> 1.11'
|
|
22
23
|
|
|
23
|
-
spec.add_development_dependency 'bundler'
|
|
24
|
+
spec.add_development_dependency 'bundler'
|
|
24
25
|
spec.add_development_dependency 'rake'
|
|
25
26
|
spec.add_development_dependency 'tailor'
|
|
26
27
|
spec.add_development_dependency 'cane'
|
|
27
28
|
spec.add_development_dependency 'countloc'
|
|
29
|
+
spec.add_development_dependency 'rspec'
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
# vim: ai et ts=2 sts=2 sw=2 ft=ruby
|
|
@@ -28,42 +28,26 @@ module Kitchen
|
|
|
28
28
|
#
|
|
29
29
|
# @author Jonathan Hartman <j@p4nt5.com>
|
|
30
30
|
class Openstack < Kitchen::Driver::SSHBase
|
|
31
|
-
default_config :name,
|
|
32
|
-
default_config :public_key_path,
|
|
33
|
-
default_config :username,
|
|
34
|
-
default_config :port,
|
|
31
|
+
default_config :name, nil
|
|
32
|
+
default_config :public_key_path, File.expand_path('~/.ssh/id_dsa.pub')
|
|
33
|
+
default_config :username, 'root'
|
|
34
|
+
default_config :port, '22'
|
|
35
|
+
default_config :openstack_tenant, nil
|
|
36
|
+
default_config :openstack_region, nil
|
|
37
|
+
default_config :openstack_service_name, nil
|
|
35
38
|
|
|
36
39
|
def create(state)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
config[:name] = "#{instance.name}-#{Etc.getlogin}-" +
|
|
40
|
-
"#{Socket.gethostname}-#{Array.new(8){rand(36).to_s(36)}.join}"
|
|
41
|
-
end
|
|
42
|
-
if config[:disable_ssl_validation]
|
|
43
|
-
require 'excon'
|
|
44
|
-
Excon.defaults[:ssl_verify_peer] = false
|
|
45
|
-
end
|
|
40
|
+
config[:name] ||= generate_name(instance.name)
|
|
41
|
+
config[:disable_ssl_validation] and disable_ssl_validation
|
|
46
42
|
server = create_server
|
|
47
43
|
state[:server_id] = server.id
|
|
48
44
|
info("OpenStack instance <#{state[:server_id]}> created.")
|
|
49
45
|
server.wait_for { print '.'; ready? } ; puts "\n(server ready)"
|
|
50
|
-
|
|
51
|
-
# server.public_ip_address stopped working in Fog 1.10.0
|
|
52
|
-
state[:hostname] = server.addresses['public'].first['addr']
|
|
53
|
-
else
|
|
54
|
-
state[:hostname] = server.addresses['private'].first['addr']
|
|
55
|
-
end
|
|
46
|
+
state[:hostname] = get_ip(server)
|
|
56
47
|
# As a consequence of IP weirdness, the OpenStack setup() method is
|
|
57
48
|
# also borked
|
|
58
|
-
ssh = Fog::SSH.new(state[:hostname], config[:username],
|
|
59
|
-
{:password => server.password})
|
|
60
|
-
pub_key = open(config[:public_key_path]).read
|
|
61
|
-
ssh.run([
|
|
62
|
-
%{mkdir .ssh},
|
|
63
|
-
%{echo "#{pub_key}" >> ~/.ssh/authorized_keys},
|
|
64
|
-
%{passwd -l #{config[:username]}}
|
|
65
|
-
])
|
|
66
49
|
wait_for_sshd(state[:hostname]) ; puts '(ssh ready)'
|
|
50
|
+
config[:key_name] or do_ssh_setup(state, config, server)
|
|
67
51
|
rescue Fog::Errors::Error, Excon::Errors::Error => ex
|
|
68
52
|
raise ActionFailed, ex.message
|
|
69
53
|
end
|
|
@@ -71,6 +55,7 @@ module Kitchen
|
|
|
71
55
|
def destroy(state)
|
|
72
56
|
return if state[:server_id].nil?
|
|
73
57
|
|
|
58
|
+
config[:disable_ssl_validation] and disable_ssl_validation
|
|
74
59
|
server = compute.servers.get(state[:server_id])
|
|
75
60
|
server.destroy unless server.nil?
|
|
76
61
|
info("OpenStack instance <#{state[:server_id]}> destroyed.")
|
|
@@ -81,22 +66,63 @@ module Kitchen
|
|
|
81
66
|
private
|
|
82
67
|
|
|
83
68
|
def compute
|
|
84
|
-
|
|
69
|
+
server_def = {
|
|
85
70
|
:provider => 'OpenStack',
|
|
86
71
|
:openstack_username => config[:openstack_username],
|
|
87
72
|
:openstack_api_key => config[:openstack_api_key],
|
|
88
|
-
:openstack_auth_url => config[:openstack_auth_url]
|
|
89
|
-
|
|
90
|
-
|
|
73
|
+
:openstack_auth_url => config[:openstack_auth_url]
|
|
74
|
+
}
|
|
75
|
+
optional = [
|
|
76
|
+
:openstack_tenant, :openstack_region, :openstack_service_name
|
|
77
|
+
]
|
|
78
|
+
optional.each do |o|
|
|
79
|
+
config[o] and server_def[o] = config[o]
|
|
80
|
+
end
|
|
81
|
+
Fog::Compute.new(server_def)
|
|
91
82
|
end
|
|
92
83
|
|
|
93
84
|
def create_server
|
|
94
|
-
|
|
95
|
-
:name
|
|
96
|
-
:image_ref
|
|
97
|
-
:flavor_ref
|
|
98
|
-
|
|
99
|
-
|
|
85
|
+
server_def = {
|
|
86
|
+
:name => config[:name],
|
|
87
|
+
:image_ref => config[:image_ref],
|
|
88
|
+
:flavor_ref => config[:flavor_ref]
|
|
89
|
+
}
|
|
90
|
+
if config[:public_key_path]
|
|
91
|
+
server_def[:public_key_path] = config[:public_key_path]
|
|
92
|
+
end
|
|
93
|
+
server_def[:key_name] = config[:key_name] if config[:key_name]
|
|
94
|
+
compute.servers.create(server_def)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def generate_name(base)
|
|
98
|
+
# Generate what should be a unique server name
|
|
99
|
+
rand_str = Array.new(8) { rand(36).to_s(36) }.join
|
|
100
|
+
"#{base}-#{Etc.getlogin}-#{Socket.gethostname}-#{rand_str}"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def get_ip(server)
|
|
104
|
+
if server.addresses['public'] and !server.addresses['public'].empty?
|
|
105
|
+
# server.public_ip_address stopped working in Fog 1.10.0
|
|
106
|
+
return server.addresses['public'].first['addr']
|
|
107
|
+
else
|
|
108
|
+
return server.addresses['private'].first['addr']
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def do_ssh_setup(state, config, server)
|
|
113
|
+
ssh = Fog::SSH.new(state[:hostname], config[:username],
|
|
114
|
+
{ :password => server.password })
|
|
115
|
+
pub_key = open(config[:public_key_path]).read
|
|
116
|
+
ssh.run([
|
|
117
|
+
%{mkdir .ssh},
|
|
118
|
+
%{echo "#{pub_key}" >> ~/.ssh/authorized_keys},
|
|
119
|
+
%{passwd -l #{config[:username]}}
|
|
120
|
+
])
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def disable_ssl_validation
|
|
124
|
+
require 'excon'
|
|
125
|
+
Excon.defaults[:ssl_verify_peer] = false
|
|
100
126
|
end
|
|
101
127
|
end
|
|
102
128
|
end
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Author:: Jonathan Hartman (<j@p4nt5.com>)
|
|
4
|
+
#
|
|
5
|
+
# Copyright (C) 2013, Jonathan Hartman
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
|
|
19
|
+
require 'logger'
|
|
20
|
+
require 'stringio'
|
|
21
|
+
require 'rspec'
|
|
22
|
+
require 'kitchen'
|
|
23
|
+
require_relative '../../spec_helper'
|
|
24
|
+
|
|
25
|
+
describe Kitchen::Driver::Openstack do
|
|
26
|
+
let(:logged_output) { StringIO.new }
|
|
27
|
+
let(:logger) { Logger.new(logged_output) }
|
|
28
|
+
let(:config) { Hash.new }
|
|
29
|
+
let(:state) { Hash.new }
|
|
30
|
+
|
|
31
|
+
let(:instance) do
|
|
32
|
+
stub(:name => 'potatoes', :logger => logger, :to_str => 'instance')
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
let(:driver) do
|
|
36
|
+
d = Kitchen::Driver::Openstack.new(config)
|
|
37
|
+
d.instance = instance
|
|
38
|
+
d
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '#initialize'do
|
|
42
|
+
context 'default options' do
|
|
43
|
+
it 'defaults to local user\'s SSH public key' do
|
|
44
|
+
expect(driver[:public_key_path]).to eq(File.expand_path(
|
|
45
|
+
'~/.ssh/id_dsa.pub'))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'defaults to SSH with root user on port 22' do
|
|
49
|
+
expect(driver[:username]).to eq('root')
|
|
50
|
+
expect(driver[:port]).to eq('22')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'defaults to no server name' do
|
|
54
|
+
expect(driver[:name]).to eq(nil)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'defaults to no tenant' do
|
|
58
|
+
expect(driver[:openstack_tenant]).to eq(nil)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'defaults to no region' do
|
|
62
|
+
expect(driver[:openstack_region]).to eq(nil)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'defaults to no service name' do
|
|
66
|
+
expect(driver[:openstack_service_name]).to eq(nil)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
context 'overridden options' do
|
|
71
|
+
let(:config) do
|
|
72
|
+
{
|
|
73
|
+
:image_ref => '22',
|
|
74
|
+
:flavor_ref => '33',
|
|
75
|
+
:public_key_path => '/tmp',
|
|
76
|
+
:username => 'admin',
|
|
77
|
+
:port => '2222',
|
|
78
|
+
:name => 'puppy',
|
|
79
|
+
:openstack_tenant => 'that_one',
|
|
80
|
+
:openstack_region => 'atlantis',
|
|
81
|
+
:openstack_service_name => 'the_service'
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'uses all the overridden options' do
|
|
86
|
+
drv = driver
|
|
87
|
+
config.each do |k, v|
|
|
88
|
+
expect(drv[k]).to eq(v)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
describe '#create' do
|
|
95
|
+
let(:server) do
|
|
96
|
+
stub(:id => 'test123', :wait_for => true,
|
|
97
|
+
:public_ip_address => '1.2.3.4')
|
|
98
|
+
end
|
|
99
|
+
let(:driver) do
|
|
100
|
+
d = Kitchen::Driver::Openstack.new(config)
|
|
101
|
+
d.instance = instance
|
|
102
|
+
d.stub(:generate_name).with('potatoes').and_return('a_monkey!')
|
|
103
|
+
d.stub(:create_server).and_return(server)
|
|
104
|
+
d.stub(:wait_for_sshd).with('1.2.3.4').and_return(true)
|
|
105
|
+
d.stub(:get_ip).and_return('1.2.3.4')
|
|
106
|
+
d.stub(:do_ssh_setup).and_return(true)
|
|
107
|
+
d
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
context 'required options provided' do
|
|
111
|
+
let(:config) do
|
|
112
|
+
{
|
|
113
|
+
:openstack_username => 'hello',
|
|
114
|
+
:openstack_api_key => 'world',
|
|
115
|
+
:openstack_auth_url => 'http:',
|
|
116
|
+
:openstack_tenant => 'www'
|
|
117
|
+
}
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it 'generates a server name in the absence of one' do
|
|
121
|
+
driver.create(state)
|
|
122
|
+
expect(driver[:name]).to eq('a_monkey!')
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it 'gets a proper server ID' do
|
|
126
|
+
driver.create(state)
|
|
127
|
+
expect(state[:server_id]).to eq('test123')
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'gets a proper hostname (IP)' do
|
|
131
|
+
driver.create(state)
|
|
132
|
+
expect(state[:hostname]).to eq('1.2.3.4')
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it 'does not disable SSL validation' do
|
|
136
|
+
driver.should_not_receive(:disable_ssl_validation)
|
|
137
|
+
driver.create(state)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
context 'SSL validation disabled' do
|
|
142
|
+
let(:config) { { :disable_ssl_validation => true } }
|
|
143
|
+
|
|
144
|
+
it 'disables SSL cert validation' do
|
|
145
|
+
driver.should_receive(:disable_ssl_validation)
|
|
146
|
+
driver.create(state)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
describe '#destroy' do
|
|
152
|
+
let(:server_id) { '12345' }
|
|
153
|
+
let(:hostname) { 'example.com' }
|
|
154
|
+
let(:state) { { :server_id => server_id, :hostname => hostname } }
|
|
155
|
+
let(:server) { stub(:nil? => false, :destroy => true) }
|
|
156
|
+
let(:servers) { stub(:get => server) }
|
|
157
|
+
let(:compute) { stub(:servers => servers) }
|
|
158
|
+
|
|
159
|
+
let(:driver) do
|
|
160
|
+
d = Kitchen::Driver::Openstack.new(config)
|
|
161
|
+
d.instance = instance
|
|
162
|
+
d.stub(:compute).and_return(compute)
|
|
163
|
+
d
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
context 'a live server that needs to be destroyed' do
|
|
167
|
+
it 'destroys the server' do
|
|
168
|
+
state.should_receive(:delete).with(:server_id)
|
|
169
|
+
state.should_receive(:delete).with(:hostname)
|
|
170
|
+
driver.destroy(state)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it 'does not disable SSL cert validation' do
|
|
174
|
+
driver.should_not_receive(:disable_ssl_validation)
|
|
175
|
+
driver.destroy(state)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
context 'no server ID present' do
|
|
180
|
+
let(:state) { Hash.new }
|
|
181
|
+
|
|
182
|
+
it 'does nothing' do
|
|
183
|
+
driver.stub(:compute)
|
|
184
|
+
driver.should_not_receive(:compute)
|
|
185
|
+
state.should_not_receive(:delete)
|
|
186
|
+
driver.destroy(state)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
context 'a server that was already destroyed' do
|
|
191
|
+
let(:servers) do
|
|
192
|
+
s = double('servers')
|
|
193
|
+
s.stub(:get).with('12345').and_return(nil)
|
|
194
|
+
s
|
|
195
|
+
end
|
|
196
|
+
let(:compute) { stub(:servers => servers) }
|
|
197
|
+
let(:driver) do
|
|
198
|
+
d = Kitchen::Driver::Openstack.new(config)
|
|
199
|
+
d.instance = instance
|
|
200
|
+
d.stub(:compute).and_return(compute)
|
|
201
|
+
d
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it 'does not try to destroy the server again' do
|
|
205
|
+
allow_message_expectations_on_nil
|
|
206
|
+
driver.destroy(state)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
context 'SSL validation disabled' do
|
|
211
|
+
let(:config) { { :disable_ssl_validation => true } }
|
|
212
|
+
|
|
213
|
+
it 'disables SSL cert validation' do
|
|
214
|
+
driver.should_receive(:disable_ssl_validation)
|
|
215
|
+
driver.destroy(state)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
describe '#compute' do
|
|
221
|
+
let(:config) do
|
|
222
|
+
{
|
|
223
|
+
:openstack_username => 'monkey',
|
|
224
|
+
:openstack_api_key => 'potato',
|
|
225
|
+
:openstack_auth_url => 'http:',
|
|
226
|
+
:openstack_tenant => 'link',
|
|
227
|
+
:openstack_region => 'ord',
|
|
228
|
+
:openstack_service_name => 'the_service'
|
|
229
|
+
}
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
context 'all requirements provided' do
|
|
233
|
+
it 'creates a new compute connection' do
|
|
234
|
+
Fog::Compute.stub(:new) { |arg| arg }
|
|
235
|
+
res = config.merge({ :provider => 'OpenStack' })
|
|
236
|
+
expect(driver.send(:compute)).to eq(res)
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
context 'only an API key provided' do
|
|
241
|
+
let(:config) { { :openstack_api_key => '1234' } }
|
|
242
|
+
|
|
243
|
+
it 'raises an error' do
|
|
244
|
+
expect { driver.send(:compute) }.to raise_error(ArgumentError)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
context 'only a username provided' do
|
|
249
|
+
let(:config) { { :openstack_username => 'monkey' } }
|
|
250
|
+
|
|
251
|
+
it 'raises an error' do
|
|
252
|
+
expect { driver.send(:compute) }.to raise_error(ArgumentError)
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
describe '#create_server' do
|
|
258
|
+
let(:config) do
|
|
259
|
+
{
|
|
260
|
+
:name => 'hello',
|
|
261
|
+
:image_ref => 'there',
|
|
262
|
+
:flavor_ref => 'captain',
|
|
263
|
+
:public_key_path => 'tarpals'
|
|
264
|
+
}
|
|
265
|
+
end
|
|
266
|
+
let(:servers) do
|
|
267
|
+
s = double('servers')
|
|
268
|
+
s.stub(:create) { |arg| arg }
|
|
269
|
+
s
|
|
270
|
+
end
|
|
271
|
+
let(:compute) { stub(:servers => servers) }
|
|
272
|
+
let(:driver) do
|
|
273
|
+
d = Kitchen::Driver::Openstack.new(config)
|
|
274
|
+
d.instance = instance
|
|
275
|
+
d.stub(:compute).and_return(compute)
|
|
276
|
+
d
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
context 'a default config' do
|
|
280
|
+
before(:each) { @config = config.dup }
|
|
281
|
+
|
|
282
|
+
it 'creates the server using a compute connection' do
|
|
283
|
+
expect(driver.send(:create_server)).to eq(@config)
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
context 'a provided public key path' do
|
|
288
|
+
let(:config) do
|
|
289
|
+
{
|
|
290
|
+
:name => 'hello',
|
|
291
|
+
:image_ref => 'there',
|
|
292
|
+
:flavor_ref => 'captain',
|
|
293
|
+
:public_key_path => 'tarpals'
|
|
294
|
+
}
|
|
295
|
+
end
|
|
296
|
+
before(:each) { @config = config.dup }
|
|
297
|
+
|
|
298
|
+
it 'passes that public key path to Fog' do
|
|
299
|
+
expect(driver.send(:create_server)).to eq(@config)
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
context 'a provided key name' do
|
|
304
|
+
let(:config) do
|
|
305
|
+
{
|
|
306
|
+
:name => 'hello',
|
|
307
|
+
:image_ref => 'there',
|
|
308
|
+
:flavor_ref => 'captain',
|
|
309
|
+
:public_key_path => 'montgomery',
|
|
310
|
+
:key_name => 'tarpals'
|
|
311
|
+
}
|
|
312
|
+
end
|
|
313
|
+
before(:each) { @config = config.dup }
|
|
314
|
+
|
|
315
|
+
it 'passes that key name to Fog' do
|
|
316
|
+
expect(driver.send(:create_server)).to eq(@config)
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
describe '#generate_name' do
|
|
322
|
+
before(:each) do
|
|
323
|
+
Etc.stub(:getlogin).and_return('user')
|
|
324
|
+
Socket.stub(:gethostname).and_return('host')
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
it 'generates a name' do
|
|
328
|
+
expect(driver.send(:generate_name, 'monkey')).to match(
|
|
329
|
+
/^monkey-user-host-/)
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
describe '#get_ip' do
|
|
334
|
+
let(:addresses) { { 'public' => [], 'private' => [] } }
|
|
335
|
+
let(:server) { stub(:addresses => addresses) }
|
|
336
|
+
|
|
337
|
+
context 'both public and private IPs' do
|
|
338
|
+
let(:addresses) do
|
|
339
|
+
{
|
|
340
|
+
'public' => [
|
|
341
|
+
{ 'addr' => '1.2.3.4' },
|
|
342
|
+
{ 'addr' => '1.2.3.5' }
|
|
343
|
+
],
|
|
344
|
+
'private' => [
|
|
345
|
+
{ 'addr' => '5.5.5.5' },
|
|
346
|
+
{ 'addr' => '6.6.6.6' }
|
|
347
|
+
]
|
|
348
|
+
}
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
it 'returns a public IP' do
|
|
352
|
+
expect(driver.send(:get_ip, server)).to eq('1.2.3.4')
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
context 'only private IPs' do
|
|
357
|
+
let(:addresses) do
|
|
358
|
+
{
|
|
359
|
+
'private' => [
|
|
360
|
+
{ 'addr' => '5.5.5.5' },
|
|
361
|
+
{ 'addr' => '6.6.6.6' }
|
|
362
|
+
]
|
|
363
|
+
}
|
|
364
|
+
end
|
|
365
|
+
it 'returns a private IP' do
|
|
366
|
+
expect(driver.send(:get_ip, server)).to eq('5.5.5.5')
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
describe '#do_ssh_setup' do
|
|
372
|
+
let(:server) { stub(:password => 'aloha') }
|
|
373
|
+
let(:state) { { :hostname => 'host' } }
|
|
374
|
+
let(:read) { stub(:read => 'a_key') }
|
|
375
|
+
let(:ssh) do
|
|
376
|
+
s = double('ssh')
|
|
377
|
+
s.stub(:run) { |args| args }
|
|
378
|
+
s
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
it 'opens an SSH session to the server' do
|
|
382
|
+
Fog::SSH.stub(:new).with('host', 'root',
|
|
383
|
+
{ :password => 'aloha' }).and_return(ssh)
|
|
384
|
+
driver.stub(:open).with(File.expand_path(
|
|
385
|
+
'~/.ssh/id_dsa.pub')).and_return(read)
|
|
386
|
+
read.stub(:read).and_return('a_key')
|
|
387
|
+
res = driver.send(:do_ssh_setup, state, config, server)
|
|
388
|
+
expected = [
|
|
389
|
+
'mkdir .ssh',
|
|
390
|
+
'echo "a_key" >> ~/.ssh/authorized_keys',
|
|
391
|
+
'passwd -l root'
|
|
392
|
+
]
|
|
393
|
+
expect(res).to eq(expected)
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
describe '#disable_ssl_validation' do
|
|
398
|
+
it 'turns off Excon SSL cert validation' do
|
|
399
|
+
expect(driver.send(:disable_ssl_validation)).to eq(false)
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# vim: ai et ts=2 sts=2 sw=2 ft=ruby
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Author:: Jonathan Hartman (<j@p4nt5.com>)
|
|
4
|
+
#
|
|
5
|
+
# Copyright (C) 2013, Jonathan Hartman
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
|
|
19
|
+
require 'rspec'
|
|
20
|
+
require_relative '../lib/kitchen/driver/openstack'
|
|
21
|
+
|
|
22
|
+
# vim: ai et ts=2 sts=2 sw=2 ft=ruby
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kitchen-openstack
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2013-
|
|
12
|
+
date: 2013-05-11 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: test-kitchen
|
|
@@ -28,21 +28,37 @@ dependencies:
|
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
29
|
version: 1.0.0.alpha
|
|
30
30
|
- !ruby/object:Gem::Dependency
|
|
31
|
-
name:
|
|
31
|
+
name: fog
|
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
|
33
33
|
none: false
|
|
34
34
|
requirements:
|
|
35
35
|
- - ~>
|
|
36
36
|
- !ruby/object:Gem::Version
|
|
37
|
-
version: '1.
|
|
38
|
-
type: :
|
|
37
|
+
version: '1.11'
|
|
38
|
+
type: :runtime
|
|
39
39
|
prerelease: false
|
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
|
41
41
|
none: false
|
|
42
42
|
requirements:
|
|
43
43
|
- - ~>
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
|
-
version: '1.
|
|
45
|
+
version: '1.11'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: bundler
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
none: false
|
|
50
|
+
requirements:
|
|
51
|
+
- - ! '>='
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
type: :development
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
none: false
|
|
58
|
+
requirements:
|
|
59
|
+
- - ! '>='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
46
62
|
- !ruby/object:Gem::Dependency
|
|
47
63
|
name: rake
|
|
48
64
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -107,6 +123,22 @@ dependencies:
|
|
|
107
123
|
- - ! '>='
|
|
108
124
|
- !ruby/object:Gem::Version
|
|
109
125
|
version: '0'
|
|
126
|
+
- !ruby/object:Gem::Dependency
|
|
127
|
+
name: rspec
|
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
|
129
|
+
none: false
|
|
130
|
+
requirements:
|
|
131
|
+
- - ! '>='
|
|
132
|
+
- !ruby/object:Gem::Version
|
|
133
|
+
version: '0'
|
|
134
|
+
type: :development
|
|
135
|
+
prerelease: false
|
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
137
|
+
none: false
|
|
138
|
+
requirements:
|
|
139
|
+
- - ! '>='
|
|
140
|
+
- !ruby/object:Gem::Version
|
|
141
|
+
version: '0'
|
|
110
142
|
description: A Test Kitchen OpenStack Nova driver
|
|
111
143
|
email:
|
|
112
144
|
- j@p4nt5.com
|
|
@@ -116,6 +148,7 @@ extra_rdoc_files: []
|
|
|
116
148
|
files:
|
|
117
149
|
- .gitignore
|
|
118
150
|
- .travis.yml
|
|
151
|
+
- CHANGELOG.md
|
|
119
152
|
- Gemfile
|
|
120
153
|
- LICENSE.txt
|
|
121
154
|
- README.md
|
|
@@ -123,6 +156,8 @@ files:
|
|
|
123
156
|
- kitchen-openstack.gemspec
|
|
124
157
|
- lib/kitchen/driver/openstack.rb
|
|
125
158
|
- lib/kitchen/driver/openstack_version.rb
|
|
159
|
+
- spec/kitchen/driver/openstack_spec.rb
|
|
160
|
+
- spec/spec_helper.rb
|
|
126
161
|
homepage: https://github.com/RoboticCheese/kitchen-openstack
|
|
127
162
|
licenses:
|
|
128
163
|
- Apache
|
|
@@ -148,5 +183,6 @@ rubygems_version: 1.8.23
|
|
|
148
183
|
signing_key:
|
|
149
184
|
specification_version: 3
|
|
150
185
|
summary: A Test Kitchen OpenStack Nova driver
|
|
151
|
-
test_files:
|
|
152
|
-
|
|
186
|
+
test_files:
|
|
187
|
+
- spec/kitchen/driver/openstack_spec.rb
|
|
188
|
+
- spec/spec_helper.rb
|