kitchen-nodes 0.2.0.dev.1 → 0.2.0.dev.2
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.
- checksums.yaml +4 -4
- data/kitchen-nodes.gemspec +3 -0
- data/lib/kitchen/provisioner/ip_finder.rb +16 -0
- data/lib/kitchen/provisioner/ip_finder/ssh.rb +93 -0
- data/lib/kitchen/provisioner/ip_finder/winrm.rb +31 -0
- data/lib/kitchen/provisioner/nodes.rb +19 -20
- data/lib/kitchen/provisioner/nodes_version.rb +1 -1
- data/spec/unit/nodes_spec.rb +176 -0
- metadata +50 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df104b8a9c65cb392ef99bd85e28f14ee62cfdb3
|
4
|
+
data.tar.gz: 65a34cfe56147e033cff2a1f26462951ed57a3ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2be4f05d1f32d5b176a5c5249da504df90c14e65e77b1799822ac95f45f83dc623dda05d28ad8a0aae913496f796884e07aaaa1cf55f8532aaf9740420636df
|
7
|
+
data.tar.gz: abf853c558c37e6ba34a88562d99835d5bff52f4f6bc6ba1452e0d06debc39c1162a96422bb4f1eda7ddad099b5304dc1b6a2e223abe577a57d724b2bc18e625
|
data/kitchen-nodes.gemspec
CHANGED
@@ -20,7 +20,10 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_dependency 'net-ping'
|
22
22
|
spec.add_dependency 'win32-security'
|
23
|
+
spec.add_dependency 'test-kitchen', '1.4.0.rc.1'
|
23
24
|
|
24
25
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
26
|
+
spec.add_development_dependency "fakefs", "~> 0.4"
|
25
27
|
spec.add_development_dependency 'rake'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.2'
|
26
29
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Kitchen
|
2
|
+
module Provisioner
|
3
|
+
module IpFinder
|
4
|
+
|
5
|
+
def self.for_transport(transport, state)
|
6
|
+
transport_string = transport.class.name.split('::').last
|
7
|
+
require("kitchen/provisioner/ip_finder/#{transport_string.downcase}")
|
8
|
+
|
9
|
+
connection = transport.connection(state)
|
10
|
+
klass = const_get(transport_string)
|
11
|
+
object = klass.new(connection)
|
12
|
+
object
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Kitchen
|
2
|
+
module Transport
|
3
|
+
class Ssh < Kitchen::Transport::Base
|
4
|
+
class Connection < Kitchen::Transport::Base::Connection
|
5
|
+
def node_execute(command, &block)
|
6
|
+
return if command.nil?
|
7
|
+
out, exit_code = node_execute_with_exit_code(command, &block)
|
8
|
+
|
9
|
+
if exit_code != 0
|
10
|
+
raise Transport::SshFailed,
|
11
|
+
"SSH exited (#{exit_code}) for command: [#{command}]"
|
12
|
+
end
|
13
|
+
out
|
14
|
+
rescue Net::SSH::Exception => ex
|
15
|
+
raise SshFailed, "SSH command failed (#{ex.message})"
|
16
|
+
end
|
17
|
+
|
18
|
+
def node_execute_with_exit_code(command, &block)
|
19
|
+
exit_code = nil
|
20
|
+
out = []
|
21
|
+
session.open_channel do |channel|
|
22
|
+
|
23
|
+
channel.request_pty
|
24
|
+
channel.exec(command) do |_ch, _success|
|
25
|
+
|
26
|
+
channel.on_data do |_ch, data|
|
27
|
+
out << data
|
28
|
+
yield data if block_given?
|
29
|
+
end
|
30
|
+
|
31
|
+
channel.on_extended_data do |_ch, _type, data|
|
32
|
+
out << data
|
33
|
+
yield data if block_given?
|
34
|
+
end
|
35
|
+
|
36
|
+
channel.on_request("exit-status") do |_ch, data|
|
37
|
+
exit_code = data.read_long
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
session.loop
|
42
|
+
[out.join("\n"), exit_code]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module Provisioner
|
49
|
+
module IpFinder
|
50
|
+
class Ssh
|
51
|
+
def initialize(connection)
|
52
|
+
@connection = connection
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_ips
|
56
|
+
run_ifconfig
|
57
|
+
rescue Kitchen::Transport::TransportFailed
|
58
|
+
run_ip_addr
|
59
|
+
end
|
60
|
+
|
61
|
+
def run_ifconfig
|
62
|
+
response = @connection.node_execute("ifconfig -a")
|
63
|
+
ips = []
|
64
|
+
start_token = "inet addr:"
|
65
|
+
response.split(/^\S+/).each do |device|
|
66
|
+
if device.include?("RUNNING") && !device.include?("LOOPBACK")
|
67
|
+
start_idx = device.index(start_token)
|
68
|
+
start_idx += start_token.length unless start_idx.nil?
|
69
|
+
end_idx = device.index(" ", start_idx) unless start_idx.nil?
|
70
|
+
ips << device[start_idx,end_idx - start_idx] unless end_idx.nil?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
ips
|
74
|
+
end
|
75
|
+
|
76
|
+
def run_ip_addr
|
77
|
+
response = @connection.node_execute("ip -4 addr show")
|
78
|
+
ips = []
|
79
|
+
start_token = "inet "
|
80
|
+
response.split(/[0-9]+: /).each do |device|
|
81
|
+
unless device.include?("LOOPBACK") || device.include?("NO-CARRIER")
|
82
|
+
start_idx = device.index(start_token)
|
83
|
+
start_idx += start_token.length unless start_idx.nil?
|
84
|
+
end_idx = device.index("/", start_idx) unless start_idx.nil?
|
85
|
+
ips << device[start_idx,end_idx - start_idx] unless end_idx.nil?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
ips
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Kitchen
|
2
|
+
module Transport
|
3
|
+
class Winrm < Kitchen::Transport::Base
|
4
|
+
class Connection < Kitchen::Transport::Base::Connection
|
5
|
+
def node_execute(command, &block)
|
6
|
+
session.run_powershell_script(command, &block)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Provisioner
|
13
|
+
module IpFinder
|
14
|
+
class Winrm
|
15
|
+
def initialize(connection)
|
16
|
+
@connection = connection
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_ips
|
20
|
+
out = @connection.node_execute("Get-NetIPConfiguration | % { $_.ipv4address.IPAddress}")
|
21
|
+
data = []
|
22
|
+
out[:data].each do |out_data|
|
23
|
+
stdout = out_data[:stdout]
|
24
|
+
data << stdout.chomp unless stdout.nil?
|
25
|
+
end
|
26
|
+
data
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -18,20 +18,10 @@
|
|
18
18
|
|
19
19
|
require "kitchen"
|
20
20
|
require "kitchen/provisioner/chef_zero"
|
21
|
+
require "kitchen/provisioner/ip_finder"
|
21
22
|
require "net/ping"
|
22
23
|
|
23
24
|
module Kitchen
|
24
|
-
|
25
|
-
module Transport
|
26
|
-
class Winrm < Kitchen::Transport::Base
|
27
|
-
class Connection < Kitchen::Transport::Base::Connection
|
28
|
-
def node_session(retry_options = {})
|
29
|
-
session(retry_options)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
25
|
module Provisioner
|
36
26
|
|
37
27
|
# Nodes provisioner for Kitchen.
|
@@ -45,12 +35,9 @@ module Kitchen
|
|
45
35
|
end
|
46
36
|
|
47
37
|
def create_node
|
48
|
-
node_dir = File.join(config[:test_base_path], "nodes")
|
49
|
-
Dir.mkdir(node_dir) unless Dir.exist?(node_dir)
|
50
|
-
node_file = File.join(node_dir, "#{instance.name}.json")
|
51
|
-
|
52
38
|
state = Kitchen::StateFile.new(config[:kitchen_root], instance.name).read
|
53
|
-
|
39
|
+
ip = state[:hostname]
|
40
|
+
ipaddress = (ip == "127.0.0.1" || ip == "localhost") ? get_reachable_guest_address(state) : ip
|
54
41
|
|
55
42
|
node = {
|
56
43
|
:id => instance.name,
|
@@ -65,14 +52,26 @@ module Kitchen
|
|
65
52
|
end
|
66
53
|
end
|
67
54
|
|
55
|
+
def node_file
|
56
|
+
node_dir = File.join(config[:test_base_path], "nodes")
|
57
|
+
Dir.mkdir(node_dir) unless Dir.exist?(node_dir)
|
58
|
+
File.join(node_dir, "#{instance.name}.json")
|
59
|
+
end
|
60
|
+
|
68
61
|
def get_reachable_guest_address(state)
|
69
|
-
instance.transport
|
70
|
-
|
71
|
-
next if address.nil? || address == "127.0.0.1"
|
62
|
+
active_ips(instance.transport, state).each do |address|
|
63
|
+
next if address == "127.0.0.1"
|
72
64
|
return address if Net::Ping::External.new.ping(address)
|
73
65
|
end
|
74
66
|
return nil
|
75
|
-
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def active_ips(transport, state)
|
70
|
+
# inject creds into state for legacy drivers
|
71
|
+
state[:password] = instance.driver[:password] if instance.driver[:password]
|
72
|
+
state[:username] = instance.driver[:username] if instance.driver[:username]
|
73
|
+
IpFinder.for_transport(transport, state).find_ips
|
74
|
+
end
|
76
75
|
end
|
77
76
|
end
|
78
77
|
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require "fakefs/safe"
|
2
|
+
require "kitchen"
|
3
|
+
require "kitchen/driver/dummy"
|
4
|
+
require "kitchen/provisioner/nodes"
|
5
|
+
require "kitchen/transport/dummy"
|
6
|
+
require "kitchen/transport/winrm"
|
7
|
+
require "kitchen/transport/ssh"
|
8
|
+
|
9
|
+
describe Kitchen::Provisioner::Nodes do
|
10
|
+
|
11
|
+
let(:config) { {
|
12
|
+
:test_base_path => "/b",
|
13
|
+
:kitchen_root => "/r",
|
14
|
+
:run_list => "cookbook:recipe"
|
15
|
+
} }
|
16
|
+
let(:instance) { double(
|
17
|
+
"instance",
|
18
|
+
:name => "test_suite",
|
19
|
+
:suite => suite,
|
20
|
+
:platform => platform,
|
21
|
+
:transport => transport,
|
22
|
+
:driver => Kitchen::Driver::Dummy.new
|
23
|
+
) }
|
24
|
+
let(:transport) { Kitchen::Transport::Dummy.new }
|
25
|
+
let(:platform) { double("platform", :os_type => nil) }
|
26
|
+
let(:suite) { double("suite", :name => "suite") }
|
27
|
+
let(:state) { { :hostname => "192.168.1.10" } }
|
28
|
+
let(:node) { JSON.parse(File.read(subject.node_file), :symbolize_names => true) }
|
29
|
+
|
30
|
+
before {
|
31
|
+
FakeFS.activate!
|
32
|
+
FileUtils.mkdir_p(config[:test_base_path])
|
33
|
+
allow_any_instance_of(Kitchen::StateFile).to receive(:read).and_return(state)
|
34
|
+
}
|
35
|
+
after {
|
36
|
+
FakeFS.deactivate!
|
37
|
+
FakeFS::FileSystem.clear
|
38
|
+
}
|
39
|
+
|
40
|
+
subject { Kitchen::Provisioner::Nodes.new(config).finalize_config!(instance) }
|
41
|
+
|
42
|
+
it "creates node" do
|
43
|
+
subject.create_node
|
44
|
+
|
45
|
+
expect(File).to exist(subject.node_file)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "sets the id" do
|
49
|
+
subject.create_node
|
50
|
+
|
51
|
+
expect(node[:id]).to eq instance.name
|
52
|
+
end
|
53
|
+
|
54
|
+
it "sets the runlist" do
|
55
|
+
subject.create_node
|
56
|
+
|
57
|
+
expect(node[:run_list]).to eq config[:run_list]
|
58
|
+
end
|
59
|
+
|
60
|
+
it "sets the ip address" do
|
61
|
+
subject.create_node
|
62
|
+
|
63
|
+
expect(node[:automatic][:ipaddress]).to eq state[:hostname]
|
64
|
+
end
|
65
|
+
|
66
|
+
context "instance is localhost" do
|
67
|
+
let(:state) { { :hostname => "127.0.0.1" } }
|
68
|
+
let(:machine_ips) { [ "192.168.1.1", "192.168.1.2", "192.168.1.3" ] }
|
69
|
+
|
70
|
+
before {
|
71
|
+
allow_any_instance_of(Net::Ping::External).to receive(:ping).and_return(true)
|
72
|
+
allow(transport).to receive(:connection).and_return(Kitchen::Transport::Base::Connection.new)
|
73
|
+
}
|
74
|
+
context "platform is windows" do
|
75
|
+
let(:transport) { Kitchen::Transport::Winrm.new }
|
76
|
+
|
77
|
+
before {
|
78
|
+
data = machine_ips.map {|ip| { :stdout => "#{ip}\r\n" }}
|
79
|
+
allow_any_instance_of(Kitchen::Transport::Base::Connection).to(
|
80
|
+
receive(:node_execute).and_return({ :data => data })
|
81
|
+
)
|
82
|
+
}
|
83
|
+
|
84
|
+
it "sets the ip address to the first reachable IP" do
|
85
|
+
subject.create_node
|
86
|
+
|
87
|
+
expect(node[:automatic][:ipaddress]).to eq machine_ips.first
|
88
|
+
end
|
89
|
+
|
90
|
+
context "only the last ip is reachable" do
|
91
|
+
before {
|
92
|
+
allow_any_instance_of(Net::Ping::External).to receive(:ping).and_return(false)
|
93
|
+
allow_any_instance_of(Net::Ping::External).to receive(:ping).with(machine_ips.last).and_return(true)
|
94
|
+
}
|
95
|
+
|
96
|
+
it "sets the ip address to the last IP" do
|
97
|
+
subject.create_node
|
98
|
+
|
99
|
+
expect(node[:automatic][:ipaddress]).to eq machine_ips.last
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "platform is *nix" do
|
105
|
+
let(:transport) { Kitchen::Transport::Ssh.new }
|
106
|
+
|
107
|
+
before {
|
108
|
+
allow_any_instance_of(Kitchen::Transport::Base::Connection).to receive(:node_execute) do
|
109
|
+
<<-EOS
|
110
|
+
docker0 Link encap:Ethernet HWaddr 56:84:7a:fe:97:99
|
111
|
+
inet addr:#{machine_ips[0]} Bcast:0.0.0.0 Mask:255.255.0.0
|
112
|
+
UP BROADCAST MULTICAST MTU:1500 Metric:1
|
113
|
+
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
|
114
|
+
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
|
115
|
+
collisions:0 txqueuelen:0
|
116
|
+
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
|
117
|
+
|
118
|
+
eth0 Link encap:Ethernet HWaddr 08:00:27:88:0c:a6
|
119
|
+
inet addr:#{machine_ips[1]} Bcast:10.0.2.255 Mask:255.255.255.0
|
120
|
+
inet6 addr: fe80::a00:27ff:fe88:ca6/64 Scope:Link
|
121
|
+
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
122
|
+
RX packets:10262 errors:0 dropped:0 overruns:0 frame:0
|
123
|
+
TX packets:7470 errors:0 dropped:0 overruns:0 carrier:0
|
124
|
+
collisions:0 txqueuelen:1000
|
125
|
+
RX bytes:1497781 (1.4 MB) TX bytes:1701791 (1.7 MB)
|
126
|
+
|
127
|
+
lo Link encap:Local Loopback
|
128
|
+
inet addr:127.0.0.1 Mask:255.0.0.0
|
129
|
+
inet6 addr: ::1/128 Scope:Host
|
130
|
+
UP LOOPBACK RUNNING MTU:65536 Metric:1
|
131
|
+
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
|
132
|
+
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
|
133
|
+
collisions:0 txqueuelen:0
|
134
|
+
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
|
135
|
+
|
136
|
+
EOS
|
137
|
+
end
|
138
|
+
}
|
139
|
+
|
140
|
+
it "sets the ip address to the RUNNING IP that is not localhost" do
|
141
|
+
subject.create_node
|
142
|
+
|
143
|
+
expect(node[:automatic][:ipaddress]).to eq machine_ips[1]
|
144
|
+
end
|
145
|
+
|
146
|
+
context "ifconfig not supported" do
|
147
|
+
before {
|
148
|
+
allow_any_instance_of(Kitchen::Transport::Base::Connection)
|
149
|
+
.to receive(:node_execute).with("ifconfig -a")
|
150
|
+
.and_raise(Kitchen::Transport::TransportFailed.new(""))
|
151
|
+
|
152
|
+
allow_any_instance_of(Kitchen::Transport::Base::Connection)
|
153
|
+
.to receive(:node_execute).with("ip -4 addr show") do
|
154
|
+
<<-EOS
|
155
|
+
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
|
156
|
+
inet 127.0.0.1/8 scope host lo
|
157
|
+
valid_lft forever preferred_lft forever
|
158
|
+
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
|
159
|
+
inet #{machine_ips[0]}/24 brd 192.168.1.255 scope global wlan0
|
160
|
+
valid_lft forever preferred_lft forever
|
161
|
+
5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
|
162
|
+
inet #{machine_ips[1]}/16 scope global docker0
|
163
|
+
valid_lft forever preferred_lft forever
|
164
|
+
EOS
|
165
|
+
end
|
166
|
+
}
|
167
|
+
|
168
|
+
it "sets the ip address to the connected IP that is not localhost" do
|
169
|
+
subject.create_node
|
170
|
+
|
171
|
+
expect(node[:automatic][:ipaddress]).to eq machine_ips[0]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kitchen-nodes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.0.dev.
|
4
|
+
version: 0.2.0.dev.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Wrock
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: net-ping
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: test-kitchen
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.4.0.rc.1
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.4.0.rc.1
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +66,20 @@ dependencies:
|
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '1.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: fakefs
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.4'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.4'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: rake
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +94,20 @@ dependencies:
|
|
66
94
|
- - ">="
|
67
95
|
- !ruby/object:Gem::Version
|
68
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.2'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.2'
|
69
111
|
description: A Test Kitchen Provisioner for Chef Nodes
|
70
112
|
email:
|
71
113
|
- matt@mattwrock.com
|
@@ -81,8 +123,12 @@ files:
|
|
81
123
|
- README.md
|
82
124
|
- Rakefile
|
83
125
|
- kitchen-nodes.gemspec
|
126
|
+
- lib/kitchen/provisioner/ip_finder.rb
|
127
|
+
- lib/kitchen/provisioner/ip_finder/ssh.rb
|
128
|
+
- lib/kitchen/provisioner/ip_finder/winrm.rb
|
84
129
|
- lib/kitchen/provisioner/nodes.rb
|
85
130
|
- lib/kitchen/provisioner/nodes_version.rb
|
131
|
+
- spec/unit/nodes_spec.rb
|
86
132
|
homepage: ''
|
87
133
|
licenses:
|
88
134
|
- Apache 2.0
|
@@ -107,5 +153,6 @@ rubygems_version: 2.4.1
|
|
107
153
|
signing_key:
|
108
154
|
specification_version: 4
|
109
155
|
summary: A Test Kitchen Provisioner for Chef Nodes
|
110
|
-
test_files:
|
156
|
+
test_files:
|
157
|
+
- spec/unit/nodes_spec.rb
|
111
158
|
has_rdoc:
|