kitchen-nodes 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/README.md +35 -3
- data/Rakefile +8 -3
- data/kitchen-nodes.gemspec +6 -5
- data/lib/kitchen/provisioner/ip_finder.rb +3 -1
- data/lib/kitchen/provisioner/ip_finder/ssh.rb +25 -28
- data/lib/kitchen/provisioner/ip_finder/winrm.rb +7 -3
- data/lib/kitchen/provisioner/nodes.rb +36 -27
- data/lib/kitchen/provisioner/nodes_version.rb +2 -4
- data/spec/unit/nodes_spec.rb +102 -109
- data/spec/unit/stubs/ifconfig.txt +26 -0
- data/spec/unit/stubs/ip.txt +9 -0
- metadata +41 -23
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bbd0dda405d103305306b0103fd135441cd4b44e
|
|
4
|
+
data.tar.gz: c809ab49ba0bbdf3e233809f6d61d88f017a2fcd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5a0c9414d16951dd2ff128a42651ddc59e1ce77af273b00bda3520154e55e8edde7d028bef389fab3905b911733ea7a520f2ab31edebe7150f0664e3409a40a6
|
|
7
|
+
data.tar.gz: 64ef1c91fd2befb4464917bf6950eb206e3bf3b97bce08b8cb017a01a941fece4e315982c0c6d2aa0e17da9a474d84346582391a533cd332485b4e01aa6f79fe
|
data/README.md
CHANGED
|
@@ -13,6 +13,9 @@ The nodes provisioner extends the `chef-zero` provisioner along with all of its
|
|
|
13
13
|
"ipaddress": "172.17.0.8",
|
|
14
14
|
"platform": "ubuntu"
|
|
15
15
|
},
|
|
16
|
+
"normal": {
|
|
17
|
+
"attr1": "val1"
|
|
18
|
+
}
|
|
16
19
|
"run_list": [
|
|
17
20
|
"recipe[apt]",
|
|
18
21
|
"recipe[couchbase-tests::ipaddress]",
|
|
@@ -36,9 +39,13 @@ The nodes provisioner extends the `chef-zero` provisioner along with all of its
|
|
|
36
39
|
}
|
|
37
40
|
```
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
The node data includes the node id based on the test-kitchen suite name, the run list assigned to the node, the normal attributes included in the `.kitchen.yml` file, the externally reachable ip address and the platform of the test instance os.
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
## <a name="installation"></a> Installation
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
gem install kitchen-nodes
|
|
48
|
+
```
|
|
42
49
|
|
|
43
50
|
## <a name="config"></a> Configuration
|
|
44
51
|
|
|
@@ -49,7 +56,7 @@ provisioner:
|
|
|
49
56
|
name: nodes
|
|
50
57
|
```
|
|
51
58
|
|
|
52
|
-
## <a name="
|
|
59
|
+
## <a name="Usage"></a> Usage
|
|
53
60
|
|
|
54
61
|
Using `kitchen-nodes` one can expect all previously converged nodes to be represented in a node file and be searchable. For example consider this scenario looking for a primary node in a cluster in order to add a node to join:
|
|
55
62
|
|
|
@@ -77,6 +84,31 @@ primary = search_for_nodes("run_list:*couchbase??server* AND platform:#{node['pl
|
|
|
77
84
|
node.normal["couchbase-tests"]["primary_ip"] = primary[0]['ipaddress']
|
|
78
85
|
|
|
79
86
|
```
|
|
87
|
+
### <a name="vagrant"></a> Using with Vagrant
|
|
88
|
+
|
|
89
|
+
When using kitchen-nodes with the vagrant driver, make sure you add the following to your `driver_config`:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
network:
|
|
93
|
+
- ["private_network", { type: "dhcp" }]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
This will add an additional non-NAT NIC to your vagrant box with an IP reachable from the host and other test nodes.
|
|
97
|
+
|
|
98
|
+
### <a name="virtualbox"></a> Why is my ohai `ipaddress` different from my node ipaddress on vagrant with VirtualBox?
|
|
99
|
+
|
|
100
|
+
Ohai will pick up the localhost ipaddress on vagrant boxes using virtualbox. To reset the `node["ipaddress"]` to the reachable ip, add `hurry-up-and-test::set_non_nat_vbox_ip` to the top of your `run_list`.
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
suites:
|
|
104
|
+
- name: my-suite
|
|
105
|
+
run_list:
|
|
106
|
+
- recipe[hurry-up-and-test::set_non_nat_vbox_ip]
|
|
107
|
+
- recipe[cookbook-under-test]
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
You can add this even if you do not use virtualbox and the recipe will do nothing.
|
|
111
|
+
|
|
80
112
|
|
|
81
113
|
## <a name="development"></a> Development
|
|
82
114
|
|
data/Rakefile
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require 'bundler/gem_tasks'
|
|
2
|
+
require 'rspec/core/rake_task'
|
|
3
|
+
require 'rubocop/rake_task'
|
|
3
4
|
|
|
4
5
|
RSpec::Core::RakeTask.new(:test)
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
RuboCop::RakeTask.new(:style) do |task|
|
|
8
|
+
task.options << '--display-cop-names'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
task default: [:test, :style]
|
data/kitchen-nodes.gemspec
CHANGED
|
@@ -8,22 +8,23 @@ Gem::Specification.new do |spec|
|
|
|
8
8
|
spec.version = Kitchen::Provisioner::NODES_VERSION
|
|
9
9
|
spec.authors = ['Matt Wrock']
|
|
10
10
|
spec.email = ['matt@mattwrock.com']
|
|
11
|
-
spec.description =
|
|
11
|
+
spec.description = 'A Test Kitchen Provisioner for Chef Nodes'
|
|
12
12
|
spec.summary = spec.description
|
|
13
13
|
spec.homepage = ''
|
|
14
14
|
spec.license = 'Apache 2.0'
|
|
15
15
|
|
|
16
|
-
spec.files = `git ls-files`.split(
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
17
17
|
spec.executables = []
|
|
18
|
-
spec.test_files = spec.files.grep(
|
|
18
|
+
spec.test_files = spec.files.grep(/^(test|spec|features)\//)
|
|
19
19
|
spec.require_paths = ['lib']
|
|
20
20
|
|
|
21
21
|
spec.add_dependency 'net-ping'
|
|
22
22
|
spec.add_dependency 'win32-security'
|
|
23
|
-
spec.add_dependency 'test-kitchen', '1.4
|
|
23
|
+
spec.add_dependency 'test-kitchen', '~> 1.4'
|
|
24
24
|
|
|
25
25
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
|
26
|
-
spec.add_development_dependency
|
|
26
|
+
spec.add_development_dependency 'fakefs', '~> 0.4'
|
|
27
27
|
spec.add_development_dependency 'rake'
|
|
28
28
|
spec.add_development_dependency 'rspec', '~> 3.2'
|
|
29
|
+
spec.add_development_dependency 'rubocop', '~> 0.29'
|
|
29
30
|
end
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
module Kitchen
|
|
2
2
|
module Provisioner
|
|
3
|
+
# Locates active IPs that are not localhost
|
|
4
|
+
# there are separate implementations for
|
|
5
|
+
# different kitchen transports
|
|
3
6
|
module IpFinder
|
|
4
|
-
|
|
5
7
|
def self.for_transport(transport, state)
|
|
6
8
|
transport_string = transport.class.name.split('::').last
|
|
7
9
|
require("kitchen/provisioner/ip_finder/#{transport_string.downcase}")
|
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
module Kitchen
|
|
2
2
|
module Transport
|
|
3
3
|
class Ssh < Kitchen::Transport::Base
|
|
4
|
+
# Monkey patch of test-kitchen ssh transport
|
|
5
|
+
# that returns stdout
|
|
4
6
|
class Connection < Kitchen::Transport::Base::Connection
|
|
5
7
|
def node_execute(command, &block)
|
|
6
8
|
return if command.nil?
|
|
7
9
|
out, exit_code = node_execute_with_exit_code(command, &block)
|
|
8
10
|
|
|
9
11
|
if exit_code != 0
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
fail Transport::SshFailed,
|
|
13
|
+
"SSH exited (#{exit_code}) for command: [#{command}]"
|
|
12
14
|
end
|
|
13
15
|
out
|
|
14
16
|
rescue Net::SSH::Exception => ex
|
|
15
17
|
raise SshFailed, "SSH command failed (#{ex.message})"
|
|
16
18
|
end
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
21
|
+
def node_execute_with_exit_code(command)
|
|
19
22
|
exit_code = nil
|
|
20
23
|
out = []
|
|
21
24
|
session.open_channel do |channel|
|
|
22
|
-
|
|
23
25
|
channel.request_pty
|
|
24
26
|
channel.exec(command) do |_ch, _success|
|
|
25
|
-
|
|
26
27
|
channel.on_data do |_ch, data|
|
|
27
28
|
out << data
|
|
28
29
|
yield data if block_given?
|
|
@@ -33,7 +34,7 @@ module Kitchen
|
|
|
33
34
|
yield data if block_given?
|
|
34
35
|
end
|
|
35
36
|
|
|
36
|
-
channel.on_request(
|
|
37
|
+
channel.on_request('exit-status') do |_ch, data|
|
|
37
38
|
exit_code = data.read_long
|
|
38
39
|
end
|
|
39
40
|
end
|
|
@@ -41,12 +42,14 @@ module Kitchen
|
|
|
41
42
|
session.loop
|
|
42
43
|
[out.join("\n"), exit_code]
|
|
43
44
|
end
|
|
45
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
|
44
46
|
end
|
|
45
47
|
end
|
|
46
48
|
end
|
|
47
49
|
|
|
48
50
|
module Provisioner
|
|
49
51
|
module IpFinder
|
|
52
|
+
# SSH implementation for returning active non-localhost IPs
|
|
50
53
|
class Ssh
|
|
51
54
|
def initialize(connection)
|
|
52
55
|
@connection = connection
|
|
@@ -54,48 +57,42 @@ module Kitchen
|
|
|
54
57
|
|
|
55
58
|
def find_ips
|
|
56
59
|
ips = []
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
while retry_count < 5
|
|
60
|
+
(0..5).each do
|
|
60
61
|
begin
|
|
61
62
|
ips = run_ifconfig
|
|
62
63
|
rescue Kitchen::Transport::TransportFailed
|
|
63
64
|
ips = run_ip_addr
|
|
64
65
|
end
|
|
65
66
|
return ips unless ips.empty?
|
|
66
|
-
retry_count += 1
|
|
67
67
|
sleep 0.5
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
def run_ifconfig
|
|
72
|
-
response = @connection.node_execute(
|
|
72
|
+
response = @connection.node_execute('ifconfig -a')
|
|
73
73
|
ips = []
|
|
74
|
-
start_token = "inet addr:"
|
|
75
74
|
response.split(/^\S+/).each do |device|
|
|
76
|
-
if device.include?(
|
|
77
|
-
|
|
78
|
-
start_idx += start_token.length unless start_idx.nil?
|
|
79
|
-
end_idx = device.index(" ", start_idx) unless start_idx.nil?
|
|
80
|
-
ips << device[start_idx,end_idx - start_idx] unless end_idx.nil?
|
|
81
|
-
end
|
|
75
|
+
next if !device.include?('RUNNING') || device.include?('LOOPBACK')
|
|
76
|
+
ips << parse_ip(device, 'inet addr:', ' ')
|
|
82
77
|
end
|
|
83
|
-
ips
|
|
78
|
+
ips.compact
|
|
84
79
|
end
|
|
85
80
|
|
|
86
81
|
def run_ip_addr
|
|
87
|
-
response = @connection.node_execute(
|
|
82
|
+
response = @connection.node_execute('ip -4 addr show')
|
|
88
83
|
ips = []
|
|
89
|
-
start_token = "inet "
|
|
90
84
|
response.split(/[0-9]+: /).each do |device|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
start_idx += start_token.length unless start_idx.nil?
|
|
94
|
-
end_idx = device.index("/", start_idx) unless start_idx.nil?
|
|
95
|
-
ips << device[start_idx,end_idx - start_idx] unless end_idx.nil?
|
|
96
|
-
end
|
|
85
|
+
next if device.include?('LOOPBACK') || device.include?('NO-CARRIER')
|
|
86
|
+
ips << parse_ip(device, 'inet ', '/')
|
|
97
87
|
end
|
|
98
|
-
ips
|
|
88
|
+
ips.compact
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def parse_ip(device, start_token, delimiter)
|
|
92
|
+
start_idx = device.index(start_token)
|
|
93
|
+
start_idx += start_token.length unless start_idx.nil?
|
|
94
|
+
end_idx = device.index(delimiter, start_idx) unless start_idx.nil?
|
|
95
|
+
device[start_idx, end_idx - start_idx] unless end_idx.nil?
|
|
99
96
|
end
|
|
100
97
|
end
|
|
101
98
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module Kitchen
|
|
2
2
|
module Transport
|
|
3
3
|
class Winrm < Kitchen::Transport::Base
|
|
4
|
+
# Monkey patch of test-kitchen winrm transport
|
|
5
|
+
# that returns stdout
|
|
4
6
|
class Connection < Kitchen::Transport::Base::Connection
|
|
5
7
|
def node_execute(command, &block)
|
|
6
8
|
session.run_powershell_script(command, &block)
|
|
@@ -11,21 +13,23 @@ module Kitchen
|
|
|
11
13
|
|
|
12
14
|
module Provisioner
|
|
13
15
|
module IpFinder
|
|
16
|
+
# WinRM implementation for returning active non-localhost IPs
|
|
14
17
|
class Winrm
|
|
15
18
|
def initialize(connection)
|
|
16
19
|
@connection = connection
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
def find_ips
|
|
20
|
-
out = @connection.node_execute(
|
|
23
|
+
out = @connection.node_execute(
|
|
24
|
+
'Get-NetIPConfiguration | % { $_.ipv4address.IPAddress }')
|
|
21
25
|
data = []
|
|
22
26
|
out[:data].each do |out_data|
|
|
23
|
-
stdout = out_data[:stdout]
|
|
27
|
+
stdout = out_data[:stdout]
|
|
24
28
|
data << stdout.chomp unless stdout.nil?
|
|
25
29
|
end
|
|
26
30
|
data
|
|
27
31
|
end
|
|
28
|
-
end
|
|
32
|
+
end
|
|
29
33
|
end
|
|
30
34
|
end
|
|
31
35
|
end
|
|
@@ -4,31 +4,29 @@
|
|
|
4
4
|
#
|
|
5
5
|
# Copyright (C) 2015, Matt Wrock
|
|
6
6
|
#
|
|
7
|
-
# Licensed under the Apache License, Version 2.0 (the
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the 'License');
|
|
8
8
|
# you may not use this file except in compliance with the License.
|
|
9
9
|
# You may obtain a copy of the License at
|
|
10
10
|
#
|
|
11
11
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
12
|
#
|
|
13
13
|
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
-
# distributed under the License is distributed on an
|
|
14
|
+
# distributed under the License is distributed on an 'AS IS' BASIS,
|
|
15
15
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
16
|
# See the License for the specific language governing permissions and
|
|
17
17
|
# limitations under the License.
|
|
18
18
|
|
|
19
|
-
require
|
|
20
|
-
require
|
|
21
|
-
require
|
|
22
|
-
require
|
|
19
|
+
require 'kitchen'
|
|
20
|
+
require 'kitchen/provisioner/chef_zero'
|
|
21
|
+
require 'kitchen/provisioner/ip_finder'
|
|
22
|
+
require 'net/ping'
|
|
23
23
|
|
|
24
24
|
module Kitchen
|
|
25
25
|
module Provisioner
|
|
26
|
-
|
|
27
26
|
# Nodes provisioner for Kitchen.
|
|
28
27
|
#
|
|
29
28
|
# @author Matt Wrock <matt@mattwrock.com>
|
|
30
29
|
class Nodes < ChefZero
|
|
31
|
-
|
|
32
30
|
def create_sandbox
|
|
33
31
|
FileUtils.rm(node_file) if File.exist?(node_file)
|
|
34
32
|
super
|
|
@@ -36,27 +34,38 @@ module Kitchen
|
|
|
36
34
|
end
|
|
37
35
|
|
|
38
36
|
def create_node
|
|
39
|
-
state = Kitchen::StateFile.new(config[:kitchen_root], instance.name).read
|
|
40
|
-
ip = state[:hostname]
|
|
41
|
-
ipaddress = (ip == "127.0.0.1" || ip == "localhost") ? get_reachable_guest_address(state) : ip
|
|
42
|
-
|
|
43
|
-
node = {
|
|
44
|
-
:id => instance.name,
|
|
45
|
-
:automatic => {
|
|
46
|
-
:ipaddress => ipaddress,
|
|
47
|
-
:platform => instance.platform.name.split("-")[0].downcase
|
|
48
|
-
},
|
|
49
|
-
:run_list => config[:run_list]
|
|
50
|
-
}
|
|
51
|
-
|
|
52
37
|
FileUtils.mkdir_p(node_dir) unless Dir.exist?(node_dir)
|
|
53
38
|
File.open(node_file, 'w') do |out|
|
|
54
|
-
out << JSON.pretty_generate(
|
|
39
|
+
out << JSON.pretty_generate(node_template)
|
|
55
40
|
end
|
|
56
41
|
end
|
|
57
42
|
|
|
43
|
+
def ipaddress
|
|
44
|
+
state = Kitchen::StateFile.new(
|
|
45
|
+
config[:kitchen_root],
|
|
46
|
+
instance.name
|
|
47
|
+
).read
|
|
48
|
+
|
|
49
|
+
if %w(127.0.0.1 localhost).include?(state[:hostname])
|
|
50
|
+
return get_reachable_guest_address(state)
|
|
51
|
+
end
|
|
52
|
+
state[:hostname]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def node_template
|
|
56
|
+
{
|
|
57
|
+
id: instance.name,
|
|
58
|
+
automatic: {
|
|
59
|
+
ipaddress: ipaddress,
|
|
60
|
+
platform: instance.platform.name.split('-')[0].downcase
|
|
61
|
+
},
|
|
62
|
+
normal: config[:attributes],
|
|
63
|
+
run_list: config[:run_list]
|
|
64
|
+
}
|
|
65
|
+
end
|
|
66
|
+
|
|
58
67
|
def node_dir
|
|
59
|
-
File.join(config[:test_base_path],
|
|
68
|
+
File.join(config[:test_base_path], 'nodes')
|
|
60
69
|
end
|
|
61
70
|
|
|
62
71
|
def node_file
|
|
@@ -65,16 +74,16 @@ module Kitchen
|
|
|
65
74
|
|
|
66
75
|
def get_reachable_guest_address(state)
|
|
67
76
|
active_ips(instance.transport, state).each do |address|
|
|
68
|
-
next if address ==
|
|
77
|
+
next if address == '127.0.0.1'
|
|
69
78
|
return address if Net::Ping::External.new.ping(address)
|
|
70
79
|
end
|
|
71
|
-
return nil
|
|
72
80
|
end
|
|
73
81
|
|
|
74
82
|
def active_ips(transport, state)
|
|
75
83
|
# inject creds into state for legacy drivers
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
[:username, :password].each do |prop|
|
|
85
|
+
state[prop] = instance.driver[prop] if instance.driver[prop]
|
|
86
|
+
end
|
|
78
87
|
IpFinder.for_transport(transport, state).find_ips
|
|
79
88
|
end
|
|
80
89
|
end
|
data/spec/unit/nodes_spec.rb
CHANGED
|
@@ -1,99 +1,116 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
1
|
+
require 'erb'
|
|
2
|
+
require 'fakefs/safe'
|
|
3
|
+
require 'kitchen'
|
|
4
|
+
require 'kitchen/driver/dummy'
|
|
5
|
+
require 'kitchen/provisioner/nodes'
|
|
6
|
+
require 'kitchen/transport/dummy'
|
|
7
|
+
require 'kitchen/transport/winrm'
|
|
8
|
+
require 'kitchen/transport/ssh'
|
|
8
9
|
|
|
9
10
|
describe Kitchen::Provisioner::Nodes do
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
let(:config) do
|
|
12
|
+
{
|
|
13
|
+
test_base_path: '/b',
|
|
14
|
+
kitchen_root: '/r',
|
|
15
|
+
run_list: 'cookbook:recipe',
|
|
16
|
+
attributes: { att_key: 'att_val' }
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
let(:instance) do
|
|
20
|
+
double(
|
|
21
|
+
'instance',
|
|
22
|
+
name: 'test_suite',
|
|
23
|
+
suite: suite,
|
|
24
|
+
platform: platform,
|
|
25
|
+
transport: transport,
|
|
26
|
+
driver: Kitchen::Driver::Dummy.new
|
|
27
|
+
)
|
|
28
|
+
end
|
|
24
29
|
let(:transport) { Kitchen::Transport::Dummy.new }
|
|
25
|
-
let(:platform) { double(
|
|
26
|
-
let(:suite) { double(
|
|
27
|
-
let(:state) { { :
|
|
28
|
-
let(:node) { JSON.parse(File.read(subject.node_file), :
|
|
30
|
+
let(:platform) { double('platform', os_type: nil, name: 'ubuntu') }
|
|
31
|
+
let(:suite) { double('suite', name: 'suite') }
|
|
32
|
+
let(:state) { { hostname: '192.168.1.10' } }
|
|
33
|
+
let(:node) { JSON.parse(File.read(subject.node_file), symbolize_names: true) }
|
|
29
34
|
|
|
30
|
-
before
|
|
35
|
+
before do
|
|
31
36
|
FakeFS.activate!
|
|
32
37
|
FileUtils.mkdir_p(config[:test_base_path])
|
|
33
|
-
allow_any_instance_of(Kitchen::StateFile)
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
allow_any_instance_of(Kitchen::StateFile)
|
|
39
|
+
.to receive(:read).and_return(state)
|
|
40
|
+
end
|
|
41
|
+
after do
|
|
36
42
|
FakeFS.deactivate!
|
|
37
43
|
FakeFS::FileSystem.clear
|
|
38
|
-
|
|
44
|
+
end
|
|
39
45
|
|
|
40
46
|
subject { Kitchen::Provisioner::Nodes.new(config).finalize_config!(instance) }
|
|
41
47
|
|
|
42
|
-
it
|
|
48
|
+
it 'creates node' do
|
|
43
49
|
subject.create_node
|
|
44
50
|
|
|
45
51
|
expect(File).to exist(subject.node_file)
|
|
46
52
|
end
|
|
47
53
|
|
|
48
|
-
it
|
|
54
|
+
it 'sets the id' do
|
|
49
55
|
subject.create_node
|
|
50
56
|
|
|
51
57
|
expect(node[:id]).to eq instance.name
|
|
52
58
|
end
|
|
53
59
|
|
|
54
|
-
it
|
|
60
|
+
it 'sets the runlist' do
|
|
55
61
|
subject.create_node
|
|
56
62
|
|
|
57
63
|
expect(node[:run_list]).to eq config[:run_list]
|
|
58
64
|
end
|
|
59
65
|
|
|
60
|
-
it
|
|
66
|
+
it 'sets the normal attributes' do
|
|
67
|
+
subject.create_node
|
|
68
|
+
|
|
69
|
+
expect(node[:normal]).to eq config[:attributes]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'sets the ip address' do
|
|
61
73
|
subject.create_node
|
|
62
74
|
|
|
63
75
|
expect(node[:automatic][:ipaddress]).to eq state[:hostname]
|
|
64
76
|
end
|
|
65
77
|
|
|
66
|
-
context
|
|
67
|
-
let(:state) { { :
|
|
68
|
-
let(:machine_ips) { [
|
|
69
|
-
|
|
70
|
-
before
|
|
71
|
-
allow_any_instance_of(Net::Ping::External).to receive(:ping)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
78
|
+
context 'instance is localhost' do
|
|
79
|
+
let(:state) { { hostname: '127.0.0.1' } }
|
|
80
|
+
let(:machine_ips) { ['192.168.1.1', '192.168.1.2', '192.168.1.3'] }
|
|
81
|
+
|
|
82
|
+
before do
|
|
83
|
+
allow_any_instance_of(Net::Ping::External).to receive(:ping)
|
|
84
|
+
.and_return(true)
|
|
85
|
+
allow(transport).to receive(:connection)
|
|
86
|
+
.and_return(Kitchen::Transport::Base::Connection.new)
|
|
87
|
+
end
|
|
88
|
+
context 'platform is windows' do
|
|
75
89
|
let(:transport) { Kitchen::Transport::Winrm.new }
|
|
76
90
|
|
|
77
|
-
before
|
|
78
|
-
data = machine_ips.map {|ip| { :
|
|
91
|
+
before do
|
|
92
|
+
data = machine_ips.map { |ip| { stdout: "#{ip}\r\n" } }
|
|
79
93
|
allow_any_instance_of(Kitchen::Transport::Base::Connection).to(
|
|
80
|
-
receive(:node_execute).and_return(
|
|
94
|
+
receive(:node_execute).and_return(data: data)
|
|
81
95
|
)
|
|
82
|
-
|
|
96
|
+
allow(platform).to receive(:name).and_return('windows')
|
|
97
|
+
end
|
|
83
98
|
|
|
84
|
-
it
|
|
99
|
+
it 'sets the ip address to the first reachable IP' do
|
|
85
100
|
subject.create_node
|
|
86
101
|
|
|
87
102
|
expect(node[:automatic][:ipaddress]).to eq machine_ips.first
|
|
88
103
|
end
|
|
89
104
|
|
|
90
|
-
context
|
|
91
|
-
before
|
|
92
|
-
allow_any_instance_of(Net::Ping::External).to receive(:ping)
|
|
93
|
-
|
|
94
|
-
|
|
105
|
+
context 'only the last ip is reachable' do
|
|
106
|
+
before do
|
|
107
|
+
allow_any_instance_of(Net::Ping::External).to receive(:ping)
|
|
108
|
+
.and_return(false)
|
|
109
|
+
allow_any_instance_of(Net::Ping::External).to receive(:ping)
|
|
110
|
+
.with(machine_ips.last).and_return(true)
|
|
111
|
+
end
|
|
95
112
|
|
|
96
|
-
it
|
|
113
|
+
it 'sets the ip address to the last IP' do
|
|
97
114
|
subject.create_node
|
|
98
115
|
|
|
99
116
|
expect(node[:automatic][:ipaddress]).to eq machine_ips.last
|
|
@@ -101,71 +118,47 @@ describe Kitchen::Provisioner::Nodes do
|
|
|
101
118
|
end
|
|
102
119
|
end
|
|
103
120
|
|
|
104
|
-
context
|
|
121
|
+
context 'platform is *nix' do
|
|
122
|
+
let(:ifconfig_response) do
|
|
123
|
+
FakeFS.deactivate!
|
|
124
|
+
template = File.read('spec/unit/stubs/ifconfig.txt')
|
|
125
|
+
FakeFS.activate!
|
|
126
|
+
template.gsub!('1.1.1.1', machine_ips[0])
|
|
127
|
+
template.gsub!('2.2.2.2', machine_ips[1])
|
|
128
|
+
end
|
|
105
129
|
let(:transport) { Kitchen::Transport::Ssh.new }
|
|
106
130
|
|
|
107
|
-
before
|
|
108
|
-
allow_any_instance_of(Kitchen::Transport::Base::Connection)
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
}
|
|
131
|
+
before do
|
|
132
|
+
allow_any_instance_of(Kitchen::Transport::Base::Connection)
|
|
133
|
+
.to receive(:node_execute).and_return(ifconfig_response)
|
|
134
|
+
end
|
|
139
135
|
|
|
140
|
-
it
|
|
136
|
+
it 'sets the ip address to the RUNNING IP that is not localhost' do
|
|
141
137
|
subject.create_node
|
|
142
138
|
|
|
143
139
|
expect(node[:automatic][:ipaddress]).to eq machine_ips[1]
|
|
144
140
|
end
|
|
145
141
|
|
|
146
|
-
context
|
|
147
|
-
|
|
142
|
+
context 'ifconfig not supported' do
|
|
143
|
+
let(:ip_response) do
|
|
144
|
+
FakeFS.deactivate!
|
|
145
|
+
template = File.read('spec/unit/stubs/ip.txt')
|
|
146
|
+
FakeFS.activate!
|
|
147
|
+
template.gsub!('1.1.1.1', machine_ips[0])
|
|
148
|
+
template.gsub!('2.2.2.2', machine_ips[1])
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
before do
|
|
148
152
|
allow_any_instance_of(Kitchen::Transport::Base::Connection)
|
|
149
|
-
|
|
150
|
-
|
|
153
|
+
.to receive(:node_execute).with('ifconfig -a')
|
|
154
|
+
.and_raise(Kitchen::Transport::TransportFailed.new(''))
|
|
151
155
|
|
|
152
156
|
allow_any_instance_of(Kitchen::Transport::Base::Connection)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
|
157
|
+
.to receive(:node_execute).with('ip -4 addr show')
|
|
158
|
+
.and_return(ip_response)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it 'sets the ip address to the connected IP that is not localhost' do
|
|
169
162
|
subject.create_node
|
|
170
163
|
|
|
171
164
|
expect(node[:automatic][:ipaddress]).to eq machine_ips[0]
|
|
@@ -173,4 +166,4 @@ lo Link encap:Local Loopback
|
|
|
173
166
|
end
|
|
174
167
|
end
|
|
175
168
|
end
|
|
176
|
-
end
|
|
169
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
docker0 Link encap:Ethernet HWaddr 56:84:7a:fe:97:99
|
|
2
|
+
inet addr:1.1.1.1 Bcast:0.0.0.0 Mask:255.255.0.0
|
|
3
|
+
UP BROADCAST MULTICAST MTU:1500 Metric:1
|
|
4
|
+
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
|
|
5
|
+
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
|
|
6
|
+
collisions:0 txqueuelen:0
|
|
7
|
+
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
|
|
8
|
+
|
|
9
|
+
eth0 Link encap:Ethernet HWaddr 08:00:27:88:0c:a6
|
|
10
|
+
inet addr:2.2.2.2 Bcast:10.0.2.255 Mask:255.255.255.0
|
|
11
|
+
inet6 addr: fe80::a00:27ff:fe88:ca6/64 Scope:Link
|
|
12
|
+
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
|
13
|
+
RX packets:10262 errors:0 dropped:0 overruns:0 frame:0
|
|
14
|
+
TX packets:7470 errors:0 dropped:0 overruns:0 carrier:0
|
|
15
|
+
collisions:0 txqueuelen:1000
|
|
16
|
+
RX bytes:1497781 (1.4 MB) TX bytes:1701791 (1.7 MB)
|
|
17
|
+
|
|
18
|
+
lo Link encap:Local Loopback
|
|
19
|
+
inet addr:127.0.0.1 Mask:255.0.0.0
|
|
20
|
+
inet6 addr: ::1/128 Scope:Host
|
|
21
|
+
UP LOOPBACK RUNNING MTU:65536 Metric:1
|
|
22
|
+
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
|
|
23
|
+
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
|
|
24
|
+
collisions:0 txqueuelen:0
|
|
25
|
+
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
|
|
26
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
|
|
2
|
+
inet 127.0.0.1/8 scope host lo
|
|
3
|
+
valid_lft forever preferred_lft forever
|
|
4
|
+
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
|
|
5
|
+
inet 1.1.1.1/24 brd 192.168.1.255 scope global wlan0
|
|
6
|
+
valid_lft forever preferred_lft forever
|
|
7
|
+
5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
|
|
8
|
+
inet 2.2.2.2/16 scope global docker0
|
|
9
|
+
valid_lft forever preferred_lft forever
|
metadata
CHANGED
|
@@ -1,113 +1,127 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kitchen-nodes
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
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-05-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: net-ping
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- -
|
|
17
|
+
- - '>='
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: '0'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- -
|
|
24
|
+
- - '>='
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: win32-security
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- -
|
|
31
|
+
- - '>='
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
33
|
version: '0'
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- -
|
|
38
|
+
- - '>='
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: test-kitchen
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
|
-
- -
|
|
45
|
+
- - ~>
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: 1.4
|
|
47
|
+
version: '1.4'
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
|
-
- -
|
|
52
|
+
- - ~>
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: 1.4
|
|
54
|
+
version: '1.4'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: bundler
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
|
-
- -
|
|
59
|
+
- - ~>
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
61
|
version: '1.3'
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
|
-
- -
|
|
66
|
+
- - ~>
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '1.3'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: fakefs
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
|
-
- -
|
|
73
|
+
- - ~>
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
75
|
version: '0.4'
|
|
76
76
|
type: :development
|
|
77
77
|
prerelease: false
|
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
|
80
|
-
- -
|
|
80
|
+
- - ~>
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '0.4'
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
84
84
|
name: rake
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
|
-
- -
|
|
87
|
+
- - '>='
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
89
|
version: '0'
|
|
90
90
|
type: :development
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
|
-
- -
|
|
94
|
+
- - '>='
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
96
|
version: '0'
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
98
|
name: rspec
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
|
101
|
-
- -
|
|
101
|
+
- - ~>
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
103
|
version: '3.2'
|
|
104
104
|
type: :development
|
|
105
105
|
prerelease: false
|
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
|
108
|
-
- -
|
|
108
|
+
- - ~>
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: '3.2'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: rubocop
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ~>
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0.29'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ~>
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0.29'
|
|
111
125
|
description: A Test Kitchen Provisioner for Chef Nodes
|
|
112
126
|
email:
|
|
113
127
|
- matt@mattwrock.com
|
|
@@ -115,8 +129,8 @@ executables: []
|
|
|
115
129
|
extensions: []
|
|
116
130
|
extra_rdoc_files: []
|
|
117
131
|
files:
|
|
118
|
-
-
|
|
119
|
-
-
|
|
132
|
+
- .gitignore
|
|
133
|
+
- .travis.yml
|
|
120
134
|
- CHANGELOG.md
|
|
121
135
|
- Gemfile
|
|
122
136
|
- LICENSE
|
|
@@ -129,6 +143,8 @@ files:
|
|
|
129
143
|
- lib/kitchen/provisioner/nodes.rb
|
|
130
144
|
- lib/kitchen/provisioner/nodes_version.rb
|
|
131
145
|
- spec/unit/nodes_spec.rb
|
|
146
|
+
- spec/unit/stubs/ifconfig.txt
|
|
147
|
+
- spec/unit/stubs/ip.txt
|
|
132
148
|
homepage: ''
|
|
133
149
|
licenses:
|
|
134
150
|
- Apache 2.0
|
|
@@ -139,20 +155,22 @@ require_paths:
|
|
|
139
155
|
- lib
|
|
140
156
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
141
157
|
requirements:
|
|
142
|
-
- -
|
|
158
|
+
- - '>='
|
|
143
159
|
- !ruby/object:Gem::Version
|
|
144
160
|
version: '0'
|
|
145
161
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
146
162
|
requirements:
|
|
147
|
-
- -
|
|
163
|
+
- - '>='
|
|
148
164
|
- !ruby/object:Gem::Version
|
|
149
165
|
version: '0'
|
|
150
166
|
requirements: []
|
|
151
167
|
rubyforge_project:
|
|
152
|
-
rubygems_version: 2.4.
|
|
168
|
+
rubygems_version: 2.4.4
|
|
153
169
|
signing_key:
|
|
154
170
|
specification_version: 4
|
|
155
171
|
summary: A Test Kitchen Provisioner for Chef Nodes
|
|
156
172
|
test_files:
|
|
157
173
|
- spec/unit/nodes_spec.rb
|
|
174
|
+
- spec/unit/stubs/ifconfig.txt
|
|
175
|
+
- spec/unit/stubs/ip.txt
|
|
158
176
|
has_rdoc:
|