nodespec 0.1.9 → 0.1.10
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/.gitignore +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +90 -0
- data/LICENSE.md +21 -0
- data/README.md +107 -0
- data/Rakefile +5 -0
- data/lib/nodespec/backend_proxy/base.rb +20 -0
- data/lib/nodespec/backend_proxy/cmd.rb +9 -0
- data/lib/nodespec/backend_proxy/exec.rb +21 -0
- data/lib/nodespec/backend_proxy/ssh.rb +28 -0
- data/lib/nodespec/backend_proxy/unixshell_utility.rb +32 -0
- data/lib/nodespec/backend_proxy/winrm.rb +23 -0
- data/lib/nodespec/backends.rb +13 -0
- data/lib/nodespec/command_execution.rb +16 -0
- data/lib/nodespec/communication_adapters/aws_ec2.rb +24 -0
- data/lib/nodespec/communication_adapters/local_backend.rb +15 -0
- data/lib/nodespec/communication_adapters/native_communicator.rb +32 -0
- data/lib/nodespec/communication_adapters/remote_backend.rb +15 -0
- data/lib/nodespec/communication_adapters/ssh.rb +14 -0
- data/lib/nodespec/communication_adapters/ssh_communicator.rb +54 -0
- data/lib/nodespec/communication_adapters/vagrant.rb +37 -0
- data/lib/nodespec/communication_adapters/winrm.rb +13 -0
- data/lib/nodespec/communication_adapters/winrm_communicator.rb +65 -0
- data/lib/nodespec/communication_adapters.rb +22 -0
- data/lib/nodespec/local_command_runner.rb +17 -0
- data/lib/nodespec/node.rb +56 -0
- data/lib/nodespec/node_configurations.rb +29 -0
- data/lib/nodespec/provisioning/ansible.rb +96 -0
- data/lib/nodespec/provisioning/chef.rb +68 -0
- data/lib/nodespec/provisioning/puppet.rb +55 -0
- data/lib/nodespec/provisioning/shellscript.rb +19 -0
- data/lib/nodespec/provisioning.rb +14 -0
- data/lib/nodespec/run_options.rb +14 -0
- data/lib/nodespec/runtime_gem_loader.rb +19 -0
- data/lib/nodespec/shared_examples_support.rb +13 -0
- data/lib/nodespec/verbose_output.rb +9 -0
- data/lib/nodespec/version.rb +3 -0
- data/nodespec.gemspec +28 -0
- data/spec/backend_proxy/base_spec.rb +29 -0
- data/spec/backend_proxy/exec_spec.rb +34 -0
- data/spec/backend_proxy/ssh_spec.rb +32 -0
- data/spec/backend_proxy/unixshell_utility_spec.rb +29 -0
- data/spec/backend_proxy/winrm_spec.rb +34 -0
- data/spec/command_execution_spec.rb +36 -0
- data/spec/communication_adapters/aws_ec2_spec.rb +70 -0
- data/spec/communication_adapters/local_backend_spec.rb +38 -0
- data/spec/communication_adapters/native_communicator_spec.rb +53 -0
- data/spec/communication_adapters/remote_backend_spec.rb +46 -0
- data/spec/communication_adapters/ssh_communicator_spec.rb +121 -0
- data/spec/communication_adapters/ssh_spec.rb +18 -0
- data/spec/communication_adapters/vagrant_spec.rb +61 -0
- data/spec/communication_adapters/winrm_communicator_spec.rb +111 -0
- data/spec/communication_adapters/winrm_spec.rb +18 -0
- data/spec/communication_adapters_spec.rb +29 -0
- data/spec/local_command_runner_spec.rb +26 -0
- data/spec/node_configurations_spec.rb +41 -0
- data/spec/node_spec.rb +110 -0
- data/spec/provisioning/ansible_spec.rb +143 -0
- data/spec/provisioning/chef_spec.rb +87 -0
- data/spec/provisioning/puppet_spec.rb +54 -0
- data/spec/provisioning/shellscript_spec.rb +20 -0
- data/spec/provisioning_spec.rb +52 -0
- data/spec/runtime_gem_loader_spec.rb +33 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/backend.rb +10 -0
- data/spec/support/init_with_current_node.rb +4 -0
- data/spec/support/local_command.rb +12 -0
- data/spec/support/node_command.rb +12 -0
- data/spec/support/ssh_communicator.rb +9 -0
- data/spec/support/winrm_communicator.rb +9 -0
- metadata +105 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be7328ed95144b05ecb62ff63b1bd38101d626ae
|
4
|
+
data.tar.gz: fd27d50ae085de35513cdef5cf12e4b6223e9180
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38a821a5b1a5db66e89d88aa04b751e34a432dfcbba50eee6dc294a59b493863628507a1a060cb93012557ac6adb433eb431aa1e5aef08c6df54cd540e7e02bd
|
7
|
+
data.tar.gz: 50f522db6244b8509713abffc8adf6f07e4c6abb1908f6ed6597af760b6062c31201492e5bc4dfda8bdd38118bd5246c3ad8aaeebb51f6d7d7c7cea4c04f38b8
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
nodespec (0.1.9)
|
5
|
+
net-ssh
|
6
|
+
serverspec
|
7
|
+
specinfra (>= 1.18.4)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
akami (1.2.2)
|
13
|
+
gyoku (>= 0.4.0)
|
14
|
+
nokogiri
|
15
|
+
aws-sdk (1.44.0)
|
16
|
+
json (~> 1.4)
|
17
|
+
nokogiri (>= 1.4.4)
|
18
|
+
builder (3.2.2)
|
19
|
+
diff-lcs (1.2.5)
|
20
|
+
ffi (1.9.3)
|
21
|
+
gssapi (1.0.3)
|
22
|
+
ffi (>= 1.0.1)
|
23
|
+
gyoku (1.1.1)
|
24
|
+
builder (>= 2.1.2)
|
25
|
+
highline (1.6.21)
|
26
|
+
httpclient (2.4.0)
|
27
|
+
httpi (0.9.7)
|
28
|
+
rack
|
29
|
+
json (1.8.1)
|
30
|
+
little-plugger (1.1.3)
|
31
|
+
logging (1.8.2)
|
32
|
+
little-plugger (>= 1.1.3)
|
33
|
+
multi_json (>= 1.8.4)
|
34
|
+
mini_portile (0.6.0)
|
35
|
+
multi_json (1.10.1)
|
36
|
+
net-ssh (2.9.1)
|
37
|
+
nokogiri (1.6.2.1)
|
38
|
+
mini_portile (= 0.6.0)
|
39
|
+
nori (1.1.5)
|
40
|
+
rack (1.5.2)
|
41
|
+
rake (10.3.2)
|
42
|
+
rspec (3.0.0)
|
43
|
+
rspec-core (~> 3.0.0)
|
44
|
+
rspec-expectations (~> 3.0.0)
|
45
|
+
rspec-mocks (~> 3.0.0)
|
46
|
+
rspec-core (3.0.2)
|
47
|
+
rspec-support (~> 3.0.0)
|
48
|
+
rspec-expectations (3.0.2)
|
49
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
50
|
+
rspec-support (~> 3.0.0)
|
51
|
+
rspec-mocks (3.0.2)
|
52
|
+
rspec-support (~> 3.0.0)
|
53
|
+
rspec-support (3.0.2)
|
54
|
+
rubyntlm (0.1.1)
|
55
|
+
savon (0.9.5)
|
56
|
+
akami (~> 1.0)
|
57
|
+
builder (>= 2.1.2)
|
58
|
+
gyoku (>= 0.4.0)
|
59
|
+
httpi (~> 0.9)
|
60
|
+
nokogiri (>= 1.4.0)
|
61
|
+
nori (~> 1.0)
|
62
|
+
wasabi (~> 1.0)
|
63
|
+
serverspec (0.15.4)
|
64
|
+
highline
|
65
|
+
net-ssh
|
66
|
+
rspec (>= 2.13.0)
|
67
|
+
specinfra (>= 0.7.1)
|
68
|
+
specinfra (1.22.0)
|
69
|
+
uuidtools (2.1.4)
|
70
|
+
wasabi (1.0.0)
|
71
|
+
nokogiri (>= 1.4.0)
|
72
|
+
winrm (1.1.3)
|
73
|
+
gssapi (~> 1.0.0)
|
74
|
+
httpclient (~> 2.2, >= 2.2.0.2)
|
75
|
+
logging (~> 1.6, >= 1.6.1)
|
76
|
+
nokogiri (~> 1.5)
|
77
|
+
rubyntlm (~> 0.1.1)
|
78
|
+
savon (= 0.9.5)
|
79
|
+
uuidtools (~> 2.1.2)
|
80
|
+
|
81
|
+
PLATFORMS
|
82
|
+
ruby
|
83
|
+
|
84
|
+
DEPENDENCIES
|
85
|
+
aws-sdk
|
86
|
+
bundler
|
87
|
+
nodespec!
|
88
|
+
rake
|
89
|
+
rspec (~> 3.0)
|
90
|
+
winrm
|
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Silvio Montanari
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
nodespec
|
2
|
+
========
|
3
|
+
|
4
|
+
RSpec style tests for multiple nodes/server instances with support for provisioning with puppet, chef, ansible
|
5
|
+
|
6
|
+
## Nodespec vs Serverspec
|
7
|
+
[Serverspec](http://serverspec.org) is a popular gem that allows you to write rspec tests to validate your servers/hosts configuration.
|
8
|
+
|
9
|
+
Nodespec is not an alternative to Serverspec, rather it's build on top of it, so you can still leverage all of its features while enjoying some extra goodies.
|
10
|
+
|
11
|
+
#### What's different
|
12
|
+
Nodespec overcomes some of the limitations of Serverspec, such as mixed OS' support (Windows and UN*X), multiple backends (Ssh and Winrm) and easy configurability and connectivity to your target hosts.
|
13
|
+
|
14
|
+
Nodespec enables a declarative way of configuring your remote connections, be it simple Ssh, a Vagrant box, or an Amazon EC2 instance.
|
15
|
+
|
16
|
+
Nodespec adds support for issuing provisiong commands to your target hosts, that could be incorporated as part of your test setup.
|
17
|
+
|
18
|
+
Below is a quick summary of the main features of nodespec. Refer to the [wiki](https://github.com/smontanari/nodespec/wiki) for more details and examples.
|
19
|
+
|
20
|
+
## Nodespec features
|
21
|
+
|
22
|
+
#### Hostname declared or inferred in specs
|
23
|
+
|
24
|
+
In serverspec you would typically have to create folders named after your target servers, whereas in nodespec you can simply declare your target server names as your spec example group:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
describe "test.example1.com" do
|
28
|
+
...
|
29
|
+
end
|
30
|
+
```
|
31
|
+
```ruby
|
32
|
+
describe "test.example2.com" do
|
33
|
+
...
|
34
|
+
end
|
35
|
+
```
|
36
|
+
#### Easy connection configuration
|
37
|
+
No more ruby code and create programmatically SSH objects, just a quick and easy inline (or file/yaml based) configuration:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
describe "test.example1.com", nodespec: {
|
41
|
+
'adapter' => 'ssh',
|
42
|
+
'user' => 'testuser',
|
43
|
+
'keys' => 'path/to/key'
|
44
|
+
} do
|
45
|
+
...
|
46
|
+
end
|
47
|
+
```
|
48
|
+
#### Support connections to Windows & Un*x servers
|
49
|
+
One of the major limitations of serverspec is that you have to make a hard decision beforehand on which OS/backend you are targeting with your tests. In particular you have to include specific specinfra modules in your `spec_helper` depending on whether you're connecting to Un\*x or Windows machines, and you cannot test both OS as part of the same spec run (unless you start hacking some conditional logic in your spec_helper, that is).
|
50
|
+
|
51
|
+
With Nodespec that problem is resolved and you can easily connect and test multiple OS and multiple backends in the same rspec run. For instance, to connect to a windows box:
|
52
|
+
```ruby
|
53
|
+
describe "test.example2.com", nodespec: {
|
54
|
+
'os' => 'Windows',
|
55
|
+
'adapter' => 'winrm',
|
56
|
+
'user' => 'testuser',
|
57
|
+
'pass' => 'somepass',
|
58
|
+
'transport' => 'ssl'
|
59
|
+
'basic_auth_only' => true
|
60
|
+
} do
|
61
|
+
...
|
62
|
+
end
|
63
|
+
```
|
64
|
+
## Provisioning (aka TDD your infrastructure)
|
65
|
+
Nodespec provides support for running Chef, Puppet, Ansible or shellscript commands as part of your tests, e.g.:
|
66
|
+
|
67
|
+
#### Chef
|
68
|
+
```ruby
|
69
|
+
describe "server1", nodespec: {'adapter' => 'vagrant'} do
|
70
|
+
before :all do
|
71
|
+
provision_node_with_chef do
|
72
|
+
set_cookbook_paths '/vshared/src/cookbooks'
|
73
|
+
set_attributes demo: {crontab: {user: 'peter'}}
|
74
|
+
chef_client_runlist 'demo::folders', 'demo::crontab'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
...
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
#### Puppet
|
82
|
+
```ruby
|
83
|
+
describe "server1", nodespec: {'adapter' => 'vagrant'} do
|
84
|
+
before :all do
|
85
|
+
provision_node_with_puppet do
|
86
|
+
set_modulepaths '/vshared/src/modules'
|
87
|
+
set_hieradata('users' => {'roger' => {'uid' => 5801}, 'peter' => {'uid' => 5802}})
|
88
|
+
puppet_apply_execute "include demo::wheel_users"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
...
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
#### Ansible
|
96
|
+
```ruby
|
97
|
+
describe "i-8f5e74r9", nodespec: {'adapter' => 'aws_ec2'} do
|
98
|
+
before :all do
|
99
|
+
provision_node_with_ansible do
|
100
|
+
enable_host_auto_discovery
|
101
|
+
set_host_key_checking false
|
102
|
+
ansible_playbook 'src/ansible/demo.yml', ['--sudo']
|
103
|
+
end
|
104
|
+
end
|
105
|
+
...
|
106
|
+
end
|
107
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'nodespec/verbose_output'
|
2
|
+
require 'nodespec/command_execution'
|
3
|
+
|
4
|
+
module NodeSpec
|
5
|
+
module BackendProxy
|
6
|
+
class Base
|
7
|
+
include CommandExecution
|
8
|
+
|
9
|
+
[:create_directory, :create_file].each do |m|
|
10
|
+
define_method(m) do |*args|
|
11
|
+
execute(send("cmd_#{m}", *args))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute(command)
|
16
|
+
raise "You must subclass #{self.class} and implement #execute"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'nodespec/run_options'
|
3
|
+
require_relative 'base'
|
4
|
+
require_relative 'unixshell_utility'
|
5
|
+
|
6
|
+
module NodeSpec
|
7
|
+
module BackendProxy
|
8
|
+
class Exec < Base
|
9
|
+
include UnixshellUtility
|
10
|
+
|
11
|
+
def execute command
|
12
|
+
command = run_as_sudo(command) if NodeSpec::RunOptions.run_local_with_sudo?
|
13
|
+
execute_within_timeout(command) do
|
14
|
+
output, status = Open3.capture2e(command)
|
15
|
+
verbose_puts(output)
|
16
|
+
status.success?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
require_relative 'unixshell_utility'
|
3
|
+
|
4
|
+
module NodeSpec
|
5
|
+
module BackendProxy
|
6
|
+
class Ssh < Base
|
7
|
+
include UnixshellUtility
|
8
|
+
|
9
|
+
ROOT_USER = 'root'
|
10
|
+
|
11
|
+
def initialize(ssh)
|
12
|
+
@ssh_session = ssh
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute(command)
|
16
|
+
command = run_as_sudo(command) if @ssh_session.options[:user] != ROOT_USER
|
17
|
+
execute_within_timeout(command) do
|
18
|
+
success = true
|
19
|
+
@ssh_session.exec!(command) do |ch, stream, data|
|
20
|
+
verbose_puts(data)
|
21
|
+
success = stream != :stderr
|
22
|
+
end
|
23
|
+
success
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module NodeSpec
|
4
|
+
module BackendProxy
|
5
|
+
module UnixshellUtility
|
6
|
+
SUDO_PREFIX = 'sudo'
|
7
|
+
TEMP_DIR = '/tmp'
|
8
|
+
|
9
|
+
def run_as_sudo(cmd)
|
10
|
+
"#{SUDO_PREFIX} #{cmd}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def cmd_create_directory(path)
|
14
|
+
shellcmd("mkdir -p #{path.shellescape}")
|
15
|
+
end
|
16
|
+
|
17
|
+
def cmd_create_file(path, content)
|
18
|
+
shellcmd("cat > #{path.shellescape} << EOF\n#{content.strip.gsub('"', '\"')}\nEOF")
|
19
|
+
end
|
20
|
+
|
21
|
+
def temp_directory
|
22
|
+
TEMP_DIR
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def shellcmd(cmd)
|
28
|
+
%Q[sh -c "#{cmd}"]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'nodespec/command_execution'
|
2
|
+
require 'nodespec/run_options'
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module NodeSpec
|
6
|
+
module BackendProxy
|
7
|
+
class WinRM < Base
|
8
|
+
def initialize(winrm)
|
9
|
+
@winrm_session = winrm
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute command
|
13
|
+
@winrm_session.set_timeout(NodeSpec::RunOptions.command_timeout)
|
14
|
+
result = @winrm_session.powershell(command)
|
15
|
+
stdout, stderr = [:stdout, :stderr].map do |s|
|
16
|
+
result[:data].select {|item| item.key? s}.map {|item| item[s]}.join
|
17
|
+
end
|
18
|
+
[stdout, stderr].each {|s| verbose_puts s}
|
19
|
+
result[:exitcode] == 0 and stderr.empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'specinfra/helper'
|
2
|
+
%w[exec cmd ssh winrm].each {|f| require_relative "backend_proxy/#{f}"}
|
3
|
+
|
4
|
+
module NodeSpec
|
5
|
+
module Backends
|
6
|
+
class SpecInfraCompatibilityError < StandardError; end
|
7
|
+
|
8
|
+
%w[Exec Ssh Cmd WinRM].each do |name|
|
9
|
+
raise SpecInfraCompatibilityError.new("module SpecInfra::Helper::#{name} is not defined!") unless SpecInfra::Helper.const_defined?(name)
|
10
|
+
const_set(name, name)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require_relative 'verbose_output'
|
3
|
+
require_relative 'run_options'
|
4
|
+
|
5
|
+
module NodeSpec
|
6
|
+
module CommandExecution
|
7
|
+
class CommandExecutionError < StandardError; end
|
8
|
+
include VerboseOutput
|
9
|
+
|
10
|
+
def execute_within_timeout(command, timeout = NodeSpec::RunOptions.command_timeout, &block)
|
11
|
+
verbose_puts "\nExecuting command:\n#{command}"
|
12
|
+
command_success = Timeout::timeout(timeout, &block)
|
13
|
+
raise CommandExecutionError.new 'The command execution failed. Enable verbosity to check the output.' unless command_success
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'nodespec/runtime_gem_loader'
|
2
|
+
require_relative 'ssh_communicator'
|
3
|
+
require_relative 'winrm_communicator'
|
4
|
+
|
5
|
+
module NodeSpec
|
6
|
+
module CommunicationAdapters
|
7
|
+
class AwsEc2
|
8
|
+
GEMLOAD_ERROR = 'In order to use any aws adapter you must install the Amazon Web Service gem'
|
9
|
+
def self.communicator_for(node_name, os = nil, options = {})
|
10
|
+
RuntimeGemLoader.require_or_fail('aws-sdk', GEMLOAD_ERROR) do
|
11
|
+
instance_name = options['instance'] || node_name
|
12
|
+
ec2_instance = AWS.ec2.instances[instance_name]
|
13
|
+
|
14
|
+
raise "EC2 Instance #{instance_name} is not reachable" unless ec2_instance.exists? && ec2_instance.status == :running
|
15
|
+
if options.has_key?('winrm')
|
16
|
+
WinrmCommunicator.new(ec2_instance.public_dns_name, os, options['winrm'])
|
17
|
+
else
|
18
|
+
SshCommunicator.new(ec2_instance.public_dns_name, os, options['ssh'] || {})
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'nodespec/backends'
|
2
|
+
|
3
|
+
module NodeSpec
|
4
|
+
module CommunicationAdapters
|
5
|
+
module LocalBackend
|
6
|
+
def backend_proxy
|
7
|
+
BackendProxy.const_get(backend).new
|
8
|
+
end
|
9
|
+
|
10
|
+
def backend
|
11
|
+
os == 'Windows' ? Backends::Cmd : Backends::Exec
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'nodespec/verbose_output'
|
2
|
+
require_relative 'local_backend'
|
3
|
+
|
4
|
+
module NodeSpec
|
5
|
+
module CommunicationAdapters
|
6
|
+
class NativeCommunicator
|
7
|
+
include LocalBackend
|
8
|
+
include VerboseOutput
|
9
|
+
|
10
|
+
attr_reader :os
|
11
|
+
|
12
|
+
def initialize(os = nil)
|
13
|
+
@os = os
|
14
|
+
end
|
15
|
+
|
16
|
+
def bind_to(configuration)
|
17
|
+
if configuration.ssh
|
18
|
+
msg = "\nClosing connection to #{configuration.ssh.host}"
|
19
|
+
msg << ":#{configuration.ssh.options[:port]}" if configuration.ssh.options[:port]
|
20
|
+
verbose_puts msg
|
21
|
+
configuration.ssh.close
|
22
|
+
configuration.ssh = nil
|
23
|
+
verbose_puts "\nRunning on local host..."
|
24
|
+
end
|
25
|
+
|
26
|
+
if configuration.winrm
|
27
|
+
configuration.winrm = nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'nodespec/backends'
|
2
|
+
|
3
|
+
module NodeSpec
|
4
|
+
module CommunicationAdapters
|
5
|
+
module RemoteBackend
|
6
|
+
def backend_proxy
|
7
|
+
BackendProxy.const_get(backend).new(session)
|
8
|
+
end
|
9
|
+
|
10
|
+
def backend
|
11
|
+
os == 'Windows' ? Backends::WinRM : Backends::Ssh
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
require_relative 'ssh_communicator'
|
3
|
+
|
4
|
+
module NodeSpec
|
5
|
+
module CommunicationAdapters
|
6
|
+
class Ssh
|
7
|
+
def self.communicator_for(node_name, os = nil, options = {})
|
8
|
+
opts = options.dup
|
9
|
+
host = opts.delete('host') || node_name
|
10
|
+
SshCommunicator.new(host, os, opts)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
require 'nodespec/verbose_output'
|
3
|
+
require_relative 'remote_backend'
|
4
|
+
|
5
|
+
module NodeSpec
|
6
|
+
module CommunicationAdapters
|
7
|
+
class SshCommunicator
|
8
|
+
include VerboseOutput
|
9
|
+
include RemoteBackend
|
10
|
+
attr_reader :session, :os
|
11
|
+
|
12
|
+
def initialize(host, os = nil, options = {})
|
13
|
+
@host = host
|
14
|
+
@os = os
|
15
|
+
@ssh_options = Net::SSH.configuration_for(@host)
|
16
|
+
@user = options['user'] || @ssh_options[:user]
|
17
|
+
%w[port password keys].each do |param|
|
18
|
+
@ssh_options[param.to_sym] = options[param] if options[param]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def bind_to(configuration)
|
23
|
+
configuration.winrm = nil if configuration.winrm
|
24
|
+
current_session = configuration.ssh
|
25
|
+
|
26
|
+
close_ssh_session(current_session) if current_session && (current_session.host != @host || current_session.options[:port] != @ssh_options[:port])
|
27
|
+
|
28
|
+
if current_session.nil? || current_session.closed?
|
29
|
+
current_session = start_new_ssh_session
|
30
|
+
configuration.host = @host
|
31
|
+
configuration.ssh = current_session
|
32
|
+
end
|
33
|
+
@session = current_session
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def close_ssh_session(session)
|
39
|
+
msg = "\nClosing connection to #{session.host}"
|
40
|
+
msg << ":#{session.options[:port]}" if session.options[:port]
|
41
|
+
verbose_puts msg
|
42
|
+
session.close
|
43
|
+
end
|
44
|
+
|
45
|
+
def start_new_ssh_session
|
46
|
+
msg = "\nConnecting to #{@host}"
|
47
|
+
msg << ":#{@ssh_options[:port]}" if @ssh_options[:port]
|
48
|
+
msg << " as #{@user}..."
|
49
|
+
verbose_puts msg
|
50
|
+
Net::SSH.start(@host, @user, @ssh_options)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require_relative 'ssh_communicator'
|
3
|
+
|
4
|
+
module NodeSpec
|
5
|
+
module CommunicationAdapters
|
6
|
+
class Vagrant
|
7
|
+
def self.communicator_for(node_name, os = nil, options = {})
|
8
|
+
vm_name = options['vm_name'] || node_name
|
9
|
+
fetch_connection_details(vm_name) do |host, opts|
|
10
|
+
SshCommunicator.new(host, os, opts)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def self.fetch_connection_details(vm_name)
|
17
|
+
cmd = "vagrant --machine-readable ssh-config #{vm_name}"
|
18
|
+
output, status = Open3.capture2e(cmd)
|
19
|
+
raise parse_error_data(output) unless status.success?
|
20
|
+
yield(parse_ssh_config_data(output)) if block_given?
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.parse_ssh_config_data(data)
|
24
|
+
/^\s*HostName\s+(?<hostname>.*)$/ =~ data
|
25
|
+
/^\s*Port\s+(?<port>\d+)$/ =~ data
|
26
|
+
/^\s*User\s+(?<username>.*)$/ =~ data
|
27
|
+
/^\s*IdentityFile\s+(?<private_key_path>.*)$/ =~ data
|
28
|
+
[hostname, {'port' => port.to_i, 'user' => username, 'keys' => private_key_path}]
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.parse_error_data(data)
|
32
|
+
/^.*,error-exit,(?<error>.*)$/ =~ data
|
33
|
+
error
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'winrm_communicator'
|
2
|
+
|
3
|
+
module NodeSpec
|
4
|
+
module CommunicationAdapters
|
5
|
+
class Winrm
|
6
|
+
def self.communicator_for(node_name, os = nil, options = {})
|
7
|
+
opts = options.dup
|
8
|
+
hostname = opts.delete('host') || node_name
|
9
|
+
WinrmCommunicator.new(hostname, os, opts)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|