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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 104ed45d9735c996dab66e5d6fe766f89d26de83
4
- data.tar.gz: ee67dc85d6d846ad09d596d836c958c07e0ac272
3
+ metadata.gz: df104b8a9c65cb392ef99bd85e28f14ee62cfdb3
4
+ data.tar.gz: 65a34cfe56147e033cff2a1f26462951ed57a3ac
5
5
  SHA512:
6
- metadata.gz: 69183b471a5fde3866af9c03795cefd17d4b69a165fb8a7352cdae1c52c7a2b90c991f17cf68aaa4c12e6002134cf97e7b4b8685d306ba78bce353b06074ff03
7
- data.tar.gz: bd99980e604067db6be768f536a2dc736f1bb1b24e81d897e5a38f5a12f5db51aa82c4a69188dbf822dcee0e8eab81125aec69ea7ff5777eb7253f7db81d5062
6
+ metadata.gz: f2be4f05d1f32d5b176a5c5249da504df90c14e65e77b1799822ac95f45f83dc623dda05d28ad8a0aae913496f796884e07aaaa1cf55f8532aaf9740420636df
7
+ data.tar.gz: abf853c558c37e6ba34a88562d99835d5bff52f4f6bc6ba1452e0d06debc39c1162a96422bb4f1eda7ddad099b5304dc1b6a2e223abe577a57d724b2bc18e625
@@ -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
- ipaddress = get_reachable_guest_address(state) || state[:hostname]
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.connection(state).node_session.run_powershell_script("Get-NetIPConfiguration | % { $_.ipv4address.IPAddress}") do |address, _|
70
- address = address.chomp unless address.nil?
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
@@ -21,6 +21,6 @@ module Kitchen
21
21
  module Provisioner
22
22
 
23
23
  # Version string for Nodes Kitchen driver
24
- NODES_VERSION = "0.2.0.dev.1"
24
+ NODES_VERSION = "0.2.0.dev.2"
25
25
  end
26
26
  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.1
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-03-28 00:00:00.000000000 Z
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: