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
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'nodespec/node_configurations'
|
2
|
+
|
3
|
+
module NodeSpec
|
4
|
+
describe NodeConfigurations do
|
5
|
+
before do
|
6
|
+
allow(File).to receive(:exists?).and_return(true)
|
7
|
+
allow(YAML).to receive(:load_file).and_return({
|
8
|
+
'test_node1' => { foo: 'bar' },
|
9
|
+
'test_node2' => { foo: 'baz' },
|
10
|
+
'test_node3' => { foo: 'qaz' }
|
11
|
+
})
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'current settings' do
|
15
|
+
context 'anything but string or hash' do
|
16
|
+
before do
|
17
|
+
allow(Node).to receive(:new).with('node_name', {}).and_return('node')
|
18
|
+
end
|
19
|
+
[nil, true, 1, [], Object.new].each do |options|
|
20
|
+
it 'returns a default Node' do
|
21
|
+
expect(NodeConfigurations.instance.get('node_name')).to eq('node')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'configuration name' do
|
27
|
+
it 'returns a Node for the given configuration name' do
|
28
|
+
allow(Node).to receive(:new).with('node_name', {foo: 'baz'}).and_return('node')
|
29
|
+
expect(NodeConfigurations.instance.get('node_name', 'test_node2')).to eq('node')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'configuration hash' do
|
34
|
+
it 'returns a Node with the given configuration' do
|
35
|
+
allow(Node).to receive(:new).with('node_name', {foo: 'baz'}).and_return('node')
|
36
|
+
expect(NodeConfigurations.instance.get('node_name', {foo: 'baz'})).to eq('node')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/spec/node_spec.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'nodespec/node'
|
2
|
+
|
3
|
+
module NodeSpec
|
4
|
+
describe Node do
|
5
|
+
shared_examples 'node os' do |os|
|
6
|
+
it "has the expected os" do
|
7
|
+
expect(subject.os).to eq(os)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
shared_examples 'run commands' do
|
12
|
+
it "runs a command through the backend proxy" do
|
13
|
+
expect(backend_proxy).to receive(:execute).with('test command')
|
14
|
+
|
15
|
+
subject.execute('test command')
|
16
|
+
end
|
17
|
+
|
18
|
+
it "creates a directory with a path relative to the temporary directory" do
|
19
|
+
expect(backend_proxy).to receive(:temp_directory).ordered.and_return('/temp/dir')
|
20
|
+
expect(backend_proxy).to receive(:create_directory).ordered.with('/temp/dir/test_dir')
|
21
|
+
|
22
|
+
expect(subject.create_temp_directory('test_dir')).to eq('/temp/dir/test_dir')
|
23
|
+
end
|
24
|
+
|
25
|
+
it "creates a directory with a path relative to the node working directory" do
|
26
|
+
expect(backend_proxy).to receive(:create_directory).ordered.with('.nodespec')
|
27
|
+
expect(backend_proxy).to receive(:create_directory).ordered.with('.nodespec/test_dir')
|
28
|
+
|
29
|
+
expect(subject.create_directory('test_dir')).to eq('.nodespec/test_dir')
|
30
|
+
end
|
31
|
+
|
32
|
+
it "writes to a file with a path relative to the node working directory" do
|
33
|
+
expect(backend_proxy).to receive(:create_directory).ordered.with('.nodespec')
|
34
|
+
expect(backend_proxy).to receive(:create_file).ordered.with('.nodespec/test/file', 'test content')
|
35
|
+
|
36
|
+
expect(subject.create_file('test/file', 'test content')).to eq('.nodespec/test/file')
|
37
|
+
end
|
38
|
+
|
39
|
+
it "creates a directory with an absolute path" do
|
40
|
+
expect(backend_proxy).to receive(:create_directory).with('/test/dir')
|
41
|
+
|
42
|
+
expect(subject.create_directory('/test/dir')).to eq('/test/dir')
|
43
|
+
end
|
44
|
+
|
45
|
+
it "writes to a file with an absolute path" do
|
46
|
+
expect(backend_proxy).to receive(:create_file).with('/test/file', 'test content')
|
47
|
+
|
48
|
+
expect(subject.create_file('/test/file', 'test content')).to eq('/test/file')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
let(:communicator) {double('communicator')}
|
53
|
+
let(:backend_proxy) {double('backend_proxy')}
|
54
|
+
|
55
|
+
it 'does not change the original options' do
|
56
|
+
Node.new('test_node', {'os' => 'test', 'foo' => 'bar'}.freeze)
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'node name' do
|
60
|
+
it 'replaces spaces with hyphen' do
|
61
|
+
subject = Node.new('test node description')
|
62
|
+
expect(subject.name).to eq('test-node-description')
|
63
|
+
end
|
64
|
+
it 'trims spaces at the end' do
|
65
|
+
subject = Node.new("test.node.description ")
|
66
|
+
expect(subject.name).to eq('test.node.description')
|
67
|
+
end
|
68
|
+
it 'cannot contain punctuation characters' do
|
69
|
+
"!@#$%^&*()+={}[]\\|:;\"'<>?,/".each_char do |invalid_char|
|
70
|
+
expect {Node.new("test #{invalid_char} name")}.to raise_error
|
71
|
+
end
|
72
|
+
end
|
73
|
+
it 'cannot start with space, underscore or hyphen characters' do
|
74
|
+
" _-".each_char do |invalid_char|
|
75
|
+
expect {Node.new("#{invalid_char} name")}.to raise_error
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe 'running commands through the communicator' do
|
81
|
+
let(:adapter) {double('adapter')}
|
82
|
+
before do
|
83
|
+
allow(communicator).to receive(:session).and_return('remote session')
|
84
|
+
allow(communicator).to receive(:backend_proxy).and_return(backend_proxy)
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'no os given' do
|
88
|
+
let(:subject) {Node.new('test_node', 'adapter' => 'test_adapter', 'foo' => 'bar')}
|
89
|
+
|
90
|
+
before do
|
91
|
+
allow(CommunicationAdapters).to receive(:get_communicator).with('test_node', nil, 'test_adapter', 'foo' => 'bar').and_return(communicator)
|
92
|
+
end
|
93
|
+
|
94
|
+
include_examples 'node os', nil
|
95
|
+
include_examples 'run commands'
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'os given' do
|
99
|
+
let(:subject) {Node.new('test_node', 'os' => 'test_os', 'adapter' => 'test_adapter', 'foo' => 'bar')}
|
100
|
+
|
101
|
+
before do
|
102
|
+
allow(CommunicationAdapters).to receive(:get_communicator).with('test_node', 'test_os', 'test_adapter', 'foo' => 'bar').and_return(communicator)
|
103
|
+
end
|
104
|
+
|
105
|
+
include_examples 'node os', 'test_os'
|
106
|
+
include_examples 'run commands'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'nodespec/provisioning/ansible'
|
3
|
+
|
4
|
+
module NodeSpec
|
5
|
+
module Provisioning
|
6
|
+
describe Ansible, init_with_current_node: true do
|
7
|
+
shared_context 'create temp file' do |prefix, path, content|
|
8
|
+
let(:tmp_file) {double('temp file')}
|
9
|
+
before do
|
10
|
+
allow(Tempfile).to receive(:new).with(prefix).and_return(tmp_file)
|
11
|
+
allow(tmp_file).to receive(:path).and_return(path)
|
12
|
+
expect(tmp_file).to receive(:write).ordered.with(content)
|
13
|
+
expect(tmp_file).to receive(:flush).ordered
|
14
|
+
expect(tmp_file).to receive(:close!).ordered
|
15
|
+
end
|
16
|
+
end
|
17
|
+
before do
|
18
|
+
allow(current_node).to receive(:name).and_return('test_host')
|
19
|
+
allow(current_node).to receive_message_chain(:communicator, :session, :options).and_return({
|
20
|
+
user: 'test_user',
|
21
|
+
keys: 'path/to user/key'
|
22
|
+
})
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'executing an ansible playbook' do
|
26
|
+
it_executes_the_local_command('ansible-playbook /path\ to/playbook -l test_host -u test_user --private-key=path/to\ user/key --sudo --opt1 --opt2') do
|
27
|
+
ansible_playbook '/path to/playbook', %w[--opt1 --opt2]
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'setting playbook variables' do
|
31
|
+
it_executes_the_local_command(/^ansible-playbook .* -e '\{"pacman":"mrs","ghosts":\["inky","pinky","clyde","sue"\]\}'$/) do
|
32
|
+
set_extra_vars pacman: 'mrs', ghosts: %w[inky pinky clyde sue]
|
33
|
+
ansible_playbook '/path to/playbook'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'executing an ansible module' do
|
39
|
+
before do
|
40
|
+
allow(current_node).to receive_message_chain(:communicator, :session, :transport, :host).and_return('test.host')
|
41
|
+
allow(current_node).to receive_message_chain(:communicator, :session, :transport, :port).and_return(1234)
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
it_executes_the_local_command('ansible test_host -m module -a module\ arguments -u test_user --private-key=path/to\ user/key --sudo --opt1 --opt2') do
|
46
|
+
ansible_module 'module', 'module arguments', %w[--opt1 --opt2]
|
47
|
+
end
|
48
|
+
|
49
|
+
describe 'running ansible' do
|
50
|
+
context 'multiples keys' do
|
51
|
+
before do
|
52
|
+
allow(current_node).to receive_message_chain(:communicator, :session, :options).and_return({
|
53
|
+
user: 'test_user',
|
54
|
+
keys: ['path/to user/key1', 'path/to user/key2']
|
55
|
+
})
|
56
|
+
end
|
57
|
+
it_executes_the_local_command('ansible test_host -m module -a module\ arguments -u test_user --private-key=path/to\ user/key1,path/to\ user/key2 --sudo --opt1 --opt2') do
|
58
|
+
ansible_module 'module', 'module arguments', %w[--opt1 --opt2]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'disable sudo' do
|
63
|
+
it_executes_the_local_command('ansible test_host -m module -a module\ arguments -u test_user --private-key=path/to\ user/key --opt1 --opt2') do
|
64
|
+
run_as_sudo(false)
|
65
|
+
ansible_module 'module', 'module arguments', %w[--opt1 --opt2]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'enable sudo' do
|
70
|
+
it_executes_the_local_command('ansible test_host -m module -a module\ arguments -u test_user --private-key=path/to\ user/key --sudo --opt1 --opt2') do
|
71
|
+
run_as_sudo
|
72
|
+
ansible_module 'module', 'module arguments', %w[--opt1 --opt2]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'runs as root without sudo' do
|
77
|
+
before do
|
78
|
+
allow(current_node).to receive_message_chain(:communicator, :session, :options).and_return({
|
79
|
+
user: 'root',
|
80
|
+
keys: 'path/to user/key'
|
81
|
+
})
|
82
|
+
end
|
83
|
+
it_executes_the_local_command('ansible test_host -m module -a module\ arguments -u root --private-key=path/to\ user/key --opt1 --opt2') do
|
84
|
+
ansible_module 'module', 'module arguments', %w[--opt1 --opt2]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'setting a path to an inventory' do
|
90
|
+
it_executes_the_local_command(/^ansible test_host .* -i path\/to\\ custom\/hosts .*/) do
|
91
|
+
set_hostfile_path 'path/to custom/hosts'
|
92
|
+
ansible_module 'module', 'module arguments', %w[--opt1 --opt2]
|
93
|
+
end
|
94
|
+
|
95
|
+
describe 'enabling inventory host auto detection' do
|
96
|
+
context 'no groups specified' do
|
97
|
+
include_context 'create temp file', 'nodespec_ansible_hosts', '/path/to/inventory', /test_host ansible_ssh_host=test.host ansible_ssh_port=1234/
|
98
|
+
|
99
|
+
it_executes_the_local_command(/^ansible test_host .* -i \/path\/to\/inventory .*/) do
|
100
|
+
enable_host_auto_discovery
|
101
|
+
ansible_module 'module', 'module arguments', %w[--opt1 --opt2]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'group specified' do
|
106
|
+
include_context 'create temp file', 'nodespec_ansible_hosts', '/path/to/inventory', <<-eos
|
107
|
+
[test-group]
|
108
|
+
test_host ansible_ssh_host=test.host ansible_ssh_port=1234
|
109
|
+
eos
|
110
|
+
|
111
|
+
it_executes_the_local_command(/^ansible test_host .* -i \/path\/to\/inventory .*/) do
|
112
|
+
enable_host_auto_discovery('test-group')
|
113
|
+
ansible_module 'module', 'module arguments', %w[--opt1 --opt2]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe 'configuring ansible' do
|
120
|
+
it_executes_the_local_command(/^ANSIBLE_HOST_KEY_CHECKING=False ansible .*/) do
|
121
|
+
set_host_key_checking(false)
|
122
|
+
ansible_module 'module', 'module arguments'
|
123
|
+
end
|
124
|
+
|
125
|
+
it_executes_the_local_command(/^ANSIBLE_CONFIG=\/path\\ to\/config ANSIBLE_HOST_KEY_CHECKING=False ansible .*/) do
|
126
|
+
set_config_path('/path to/config')
|
127
|
+
set_host_key_checking(false)
|
128
|
+
ansible_module 'module', 'module arguments'
|
129
|
+
end
|
130
|
+
|
131
|
+
describe 'inline custom configuration' do
|
132
|
+
include_context 'create temp file', 'nodespec_ansible_cfg', '/path/to/cfg', 'test config'
|
133
|
+
|
134
|
+
it_executes_the_local_command(/^ANSIBLE_CONFIG=\/path\/to\/cfg ansible .*/) do
|
135
|
+
ansible_config('test config')
|
136
|
+
ansible_module 'module', 'module arguments'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'nodespec/provisioning/chef'
|
3
|
+
|
4
|
+
module NodeSpec
|
5
|
+
module Provisioning
|
6
|
+
describe Chef, init_with_current_node: true do
|
7
|
+
describe 'running chef-apply' do
|
8
|
+
it_executes_the_node_command('chef-apply --opt1 --opt2 -e include_recipe\ \"main::cron\"') do
|
9
|
+
chef_apply_execute 'include_recipe "main::cron"', %w[--opt1 --opt2]
|
10
|
+
end
|
11
|
+
|
12
|
+
it_executes_the_node_command('chef-apply /test\ path/to/recipe --opt1 --opt2') do
|
13
|
+
chef_apply_recipe '/test path/to/recipe', %w[--opt1 --opt2]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'running chef-client' do
|
18
|
+
context 'specified node path' do
|
19
|
+
describe 'custom configuration' do
|
20
|
+
before do
|
21
|
+
expect(current_node).to receive(:create_file).with('chef_client.rb', "node_path '/path/to/nodes'").ordered.and_return('config/chef_client.rb')
|
22
|
+
end
|
23
|
+
|
24
|
+
it_executes_the_node_command(
|
25
|
+
'chef-client -z --opt1 --opt2 -c config/chef_client.rb -o recipe1,recipe2'
|
26
|
+
) do
|
27
|
+
chef_client_config "node_path '/path/to/nodes'"
|
28
|
+
chef_client_runlist 'recipe1', 'recipe2', %w[--opt1 --opt2]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'implicit node path' do
|
34
|
+
before do
|
35
|
+
expect(current_node).to receive(:create_temp_directory).with('chef_nodes').and_return('/tmp/dir/chef_nodes')
|
36
|
+
allow(current_node).to receive(:create_file).with('chef_client.rb', /^node_path '\/tmp\/dir\/chef_nodes'$/).and_return('config/chef_client.rb')
|
37
|
+
end
|
38
|
+
|
39
|
+
it_executes_the_node_command('chef-client -z --opt1 --opt2 -c config/chef_client.rb -o recipe1,recipe2') do
|
40
|
+
chef_client_runlist 'recipe1', 'recipe2', %w[--opt1 --opt2]
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'custom configuration' do
|
44
|
+
before do
|
45
|
+
expect(current_node).to receive(:create_file).with('chef_client.rb', /^test config$/).ordered.and_return('config/chef_client.rb')
|
46
|
+
end
|
47
|
+
|
48
|
+
it_executes_the_node_command(
|
49
|
+
'chef-client -z --opt1 --opt2 -c config/chef_client.rb -o recipe1,recipe2'
|
50
|
+
) do
|
51
|
+
chef_client_config 'test config'
|
52
|
+
chef_client_runlist 'recipe1', 'recipe2', %w[--opt1 --opt2]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'custom cookbook path' do
|
57
|
+
before do
|
58
|
+
expect(current_node).to receive(:create_file).with('chef_client.rb', /^cookbook_path \['\/var\/chef\/cookbooks','\/var\/chef\/site-cookbooks'\]$/).ordered.and_return('config/chef_client.rb')
|
59
|
+
end
|
60
|
+
|
61
|
+
it_executes_the_node_command(
|
62
|
+
'chef-client -z --opt1 --opt2 -c config/chef_client.rb -o recipe1,recipe2'
|
63
|
+
) do
|
64
|
+
set_cookbook_paths '/var/chef/cookbooks', '/var/chef/site-cookbooks'
|
65
|
+
|
66
|
+
chef_client_runlist 'recipe1', 'recipe2', %w[--opt1 --opt2]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe 'setting custom attributes' do
|
71
|
+
before do
|
72
|
+
expect(current_node).to receive(:create_file).with('chef_client_attributes.json', %q[{"test_attributes":{"attr1":"foo","attr2":"bar"}}]).ordered.and_return('config/chef_client_attributes.json')
|
73
|
+
end
|
74
|
+
|
75
|
+
it_executes_the_node_command(
|
76
|
+
'chef-client -z -c config/chef_client.rb -j config/chef_client_attributes.json -o recipe1,recipe2'
|
77
|
+
) do
|
78
|
+
set_attributes test_attributes: {attr1: 'foo', attr2: 'bar'}
|
79
|
+
|
80
|
+
chef_client_runlist 'recipe1', 'recipe2'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
require 'nodespec/provisioning/puppet'
|
3
|
+
|
4
|
+
module NodeSpec
|
5
|
+
module Provisioning
|
6
|
+
describe Puppet, init_with_current_node: true do
|
7
|
+
describe 'executing a puppet inline code' do
|
8
|
+
it_executes_the_node_command(/^puppet apply\s+ --opt1 --opt2 -e include\\ testmodule::testclass$/) do
|
9
|
+
puppet_apply_execute 'include testmodule::testclass', %w[--opt1 --opt2]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'executing a puppet manifest file' do
|
14
|
+
it_executes_the_node_command(/^puppet apply .* \/test\/path\\ to\/manifest$/) do
|
15
|
+
puppet_apply_manifest '/test/path to/manifest'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'setting facts' do
|
20
|
+
it_executes_the_node_command(/^FACTER_foo=bar\\ baz puppet apply .*$/) do
|
21
|
+
set_facts 'foo' => 'bar baz'
|
22
|
+
puppet_apply_manifest '/test/path/to/manifest'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'setting the module path' do
|
27
|
+
it_executes_the_node_command(/puppet apply --modulepath \/test\\ path\/1:\/test\\ path\/2 .*/) do
|
28
|
+
set_modulepaths '/test path/1', '/test path/2'
|
29
|
+
puppet_apply_execute 'include testmodule::testclass'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'setting hiera data' do
|
34
|
+
before do
|
35
|
+
expect(current_node).to receive(:create_directory).with('puppet_hieradata').ordered.and_return('config/puppet_hieradata')
|
36
|
+
expect(current_node).to receive(:create_file).with('puppet_hieradata/common.yaml', "---\ntest: hiera data\n").ordered.and_return('config/puppet_hieradata/common.yaml')
|
37
|
+
expect(current_node).to receive(:create_file).with('puppet_hiera.yaml', ":backends:\n - yaml\n:yaml:\n :datadir: config/puppet_hieradata\n:hierarchy:\n - common\n").ordered.and_return('config/puppet_hiera.yaml')
|
38
|
+
end
|
39
|
+
|
40
|
+
it_executes_the_node_command(
|
41
|
+
'puppet apply --modulepath /test/module/path --hiera_config config/puppet_hiera.yaml --opts /test/path/to/manifest'
|
42
|
+
) do
|
43
|
+
set_modulepaths '/test/module/path'
|
44
|
+
set_hieradata('test' => 'hiera data')
|
45
|
+
|
46
|
+
puppet_apply_manifest '/test/path/to/manifest', %w[--opts]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'setting hiera config' do
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'nodespec/provisioning/shellscript'
|
3
|
+
|
4
|
+
module NodeSpec
|
5
|
+
module Provisioning
|
6
|
+
describe Shellscript, init_with_current_node: true do
|
7
|
+
# let()
|
8
|
+
it_executes_the_node_command('/path/to/test/script.sh') do
|
9
|
+
execute_file('/path/to/test/script.sh')
|
10
|
+
end
|
11
|
+
it_executes_the_node_command("sh -c mkdir\\ /tmp/test_dir'\n'cd\\ /tmp/test_dir'\n'touch\\ test.txt'\n'") do
|
12
|
+
execute_script <<-EOS
|
13
|
+
mkdir /tmp/test_dir
|
14
|
+
cd /tmp/test_dir
|
15
|
+
touch test.txt
|
16
|
+
EOS
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'nodespec/provisioning'
|
2
|
+
|
3
|
+
module NodeSpec
|
4
|
+
describe Provisioning do
|
5
|
+
available_provisioners = {
|
6
|
+
'puppet' => Provisioning::Puppet,
|
7
|
+
'chef' => Provisioning::Chef,
|
8
|
+
'ansible' => Provisioning::Ansible,
|
9
|
+
'shellscript' => Provisioning::Shellscript
|
10
|
+
}
|
11
|
+
|
12
|
+
let(:subject) {Object.new.extend Provisioning}
|
13
|
+
let(:provisioning_block) {Proc.new {}}
|
14
|
+
|
15
|
+
before do
|
16
|
+
allow(NodeSpec).to receive(:current_node).and_return('current node')
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'multiple invocations to same provisioner' do
|
20
|
+
let(:provisioner_instance){double('provisioner instance')}
|
21
|
+
available_provisioners.each do |name, clazz|
|
22
|
+
it "instantiate the #{clazz} provisioner once only" do
|
23
|
+
allow(clazz).to receive(:new).with('current node').once.and_return(provisioner_instance)
|
24
|
+
expect(provisioner_instance).to receive(:instance_eval).twice
|
25
|
+
|
26
|
+
subject.send("provision_node_with_#{name}", &provisioning_block)
|
27
|
+
subject.send("provision_node_with_#{name}", &provisioning_block)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "executes the #{clazz} command" do
|
31
|
+
allow(clazz).to receive(:new).with('current node').and_return(provisioner_instance)
|
32
|
+
expect(provisioner_instance).to receive(:instance_eval) do |&b|
|
33
|
+
expect(b).to be provisioning_block
|
34
|
+
end
|
35
|
+
|
36
|
+
subject.send("provision_node_with_#{name}", &provisioning_block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "multiple provisioners" do
|
42
|
+
it 'allows to run multiple provisioners on the same node' do
|
43
|
+
available_provisioners.each do |name, clazz|
|
44
|
+
provisioner_instance = double("#{name} provisioner instance")
|
45
|
+
allow(clazz).to receive(:new).with('current node').once.and_return(provisioner_instance)
|
46
|
+
expect(provisioner_instance).to receive(:instance_eval)
|
47
|
+
subject.send("provision_node_with_#{name}", &provisioning_block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'nodespec/runtime_gem_loader'
|
2
|
+
|
3
|
+
module NodeSpec
|
4
|
+
describe RuntimeGemLoader do
|
5
|
+
it 'requires the gem successfully and executes the block' do
|
6
|
+
success = false
|
7
|
+
RuntimeGemLoader.require_or_fail('rspec') do
|
8
|
+
success = true
|
9
|
+
end
|
10
|
+
expect(success).to be_truthy
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'fails to require the gem and prints a default error message' do
|
14
|
+
success = false
|
15
|
+
expect do
|
16
|
+
RuntimeGemLoader.require_or_fail('a-gem-that/does-not-exist') do
|
17
|
+
success = true
|
18
|
+
end
|
19
|
+
end.to raise_error(/Consider installing the missing gem\s+gem install 'a-gem-that\/does-not-exist'/)
|
20
|
+
expect(success).to be_falsy
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'fails to require the gem and prints a custom error message' do
|
24
|
+
success = false
|
25
|
+
expect do
|
26
|
+
RuntimeGemLoader.require_or_fail('a-gem-that/does-not-exist', 'this is wrong') do
|
27
|
+
success = true
|
28
|
+
end
|
29
|
+
end.to raise_error(/this is wrong\s+gem install 'a-gem-that\/does-not-exist'/)
|
30
|
+
expect(success).to be_falsy
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
shared_examples 'providing a backend' do |expected_backend|
|
2
|
+
it "returns #{expected_backend} backend" do
|
3
|
+
expect(subject.backend).to eq(expected_backend)
|
4
|
+
end
|
5
|
+
|
6
|
+
it "returns BackendProxy::#{expected_backend} as backend proxy" do
|
7
|
+
expect(subject.backend_proxy).to be_a(NodeSpec::BackendProxy.const_get(expected_backend))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module NodeSpec
|
2
|
+
module RSpecExtensions
|
3
|
+
def it_executes_the_local_command(*command_matches, &instructions)
|
4
|
+
it 'runs the commands' do
|
5
|
+
command_matches.each do |command_match|
|
6
|
+
expect(subject).to receive(:run_command).with(command_match)
|
7
|
+
end
|
8
|
+
subject.instance_eval(&instructions)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module NodeSpec
|
2
|
+
module RSpecExtensions
|
3
|
+
def it_executes_the_node_command(*command_matches, &provisioning_instructions)
|
4
|
+
it 'runs the commands' do
|
5
|
+
command_matches.each do |command_match|
|
6
|
+
expect(current_node).to receive(:execute).with(command_match)
|
7
|
+
end
|
8
|
+
subject.instance_eval(&provisioning_instructions)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'nodespec/communication_adapters/ssh_communicator'
|
2
|
+
|
3
|
+
shared_examples 'new_ssh_communicator' do |hostname, os, options|
|
4
|
+
before do
|
5
|
+
allow(NodeSpec::CommunicationAdapters::SshCommunicator).to receive(:new).with(
|
6
|
+
hostname, os, options
|
7
|
+
).and_return('ssh communicator')
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'nodespec/communication_adapters/winrm_communicator'
|
2
|
+
|
3
|
+
shared_context 'new_winrm_communicator' do |hostname, os, options|
|
4
|
+
before do
|
5
|
+
allow(NodeSpec::CommunicationAdapters::WinrmCommunicator).to receive(:new).with(
|
6
|
+
hostname, os, options
|
7
|
+
).and_return('winrm communicator')
|
8
|
+
end
|
9
|
+
end
|