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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +90 -0
  5. data/LICENSE.md +21 -0
  6. data/README.md +107 -0
  7. data/Rakefile +5 -0
  8. data/lib/nodespec/backend_proxy/base.rb +20 -0
  9. data/lib/nodespec/backend_proxy/cmd.rb +9 -0
  10. data/lib/nodespec/backend_proxy/exec.rb +21 -0
  11. data/lib/nodespec/backend_proxy/ssh.rb +28 -0
  12. data/lib/nodespec/backend_proxy/unixshell_utility.rb +32 -0
  13. data/lib/nodespec/backend_proxy/winrm.rb +23 -0
  14. data/lib/nodespec/backends.rb +13 -0
  15. data/lib/nodespec/command_execution.rb +16 -0
  16. data/lib/nodespec/communication_adapters/aws_ec2.rb +24 -0
  17. data/lib/nodespec/communication_adapters/local_backend.rb +15 -0
  18. data/lib/nodespec/communication_adapters/native_communicator.rb +32 -0
  19. data/lib/nodespec/communication_adapters/remote_backend.rb +15 -0
  20. data/lib/nodespec/communication_adapters/ssh.rb +14 -0
  21. data/lib/nodespec/communication_adapters/ssh_communicator.rb +54 -0
  22. data/lib/nodespec/communication_adapters/vagrant.rb +37 -0
  23. data/lib/nodespec/communication_adapters/winrm.rb +13 -0
  24. data/lib/nodespec/communication_adapters/winrm_communicator.rb +65 -0
  25. data/lib/nodespec/communication_adapters.rb +22 -0
  26. data/lib/nodespec/local_command_runner.rb +17 -0
  27. data/lib/nodespec/node.rb +56 -0
  28. data/lib/nodespec/node_configurations.rb +29 -0
  29. data/lib/nodespec/provisioning/ansible.rb +96 -0
  30. data/lib/nodespec/provisioning/chef.rb +68 -0
  31. data/lib/nodespec/provisioning/puppet.rb +55 -0
  32. data/lib/nodespec/provisioning/shellscript.rb +19 -0
  33. data/lib/nodespec/provisioning.rb +14 -0
  34. data/lib/nodespec/run_options.rb +14 -0
  35. data/lib/nodespec/runtime_gem_loader.rb +19 -0
  36. data/lib/nodespec/shared_examples_support.rb +13 -0
  37. data/lib/nodespec/verbose_output.rb +9 -0
  38. data/lib/nodespec/version.rb +3 -0
  39. data/nodespec.gemspec +28 -0
  40. data/spec/backend_proxy/base_spec.rb +29 -0
  41. data/spec/backend_proxy/exec_spec.rb +34 -0
  42. data/spec/backend_proxy/ssh_spec.rb +32 -0
  43. data/spec/backend_proxy/unixshell_utility_spec.rb +29 -0
  44. data/spec/backend_proxy/winrm_spec.rb +34 -0
  45. data/spec/command_execution_spec.rb +36 -0
  46. data/spec/communication_adapters/aws_ec2_spec.rb +70 -0
  47. data/spec/communication_adapters/local_backend_spec.rb +38 -0
  48. data/spec/communication_adapters/native_communicator_spec.rb +53 -0
  49. data/spec/communication_adapters/remote_backend_spec.rb +46 -0
  50. data/spec/communication_adapters/ssh_communicator_spec.rb +121 -0
  51. data/spec/communication_adapters/ssh_spec.rb +18 -0
  52. data/spec/communication_adapters/vagrant_spec.rb +61 -0
  53. data/spec/communication_adapters/winrm_communicator_spec.rb +111 -0
  54. data/spec/communication_adapters/winrm_spec.rb +18 -0
  55. data/spec/communication_adapters_spec.rb +29 -0
  56. data/spec/local_command_runner_spec.rb +26 -0
  57. data/spec/node_configurations_spec.rb +41 -0
  58. data/spec/node_spec.rb +110 -0
  59. data/spec/provisioning/ansible_spec.rb +143 -0
  60. data/spec/provisioning/chef_spec.rb +87 -0
  61. data/spec/provisioning/puppet_spec.rb +54 -0
  62. data/spec/provisioning/shellscript_spec.rb +20 -0
  63. data/spec/provisioning_spec.rb +52 -0
  64. data/spec/runtime_gem_loader_spec.rb +33 -0
  65. data/spec/spec_helper.rb +5 -0
  66. data/spec/support/backend.rb +10 -0
  67. data/spec/support/init_with_current_node.rb +4 -0
  68. data/spec/support/local_command.rb +12 -0
  69. data/spec/support/node_command.rb +12 -0
  70. data/spec/support/ssh_communicator.rb +9 -0
  71. data/spec/support/winrm_communicator.rb +9 -0
  72. 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
@@ -0,0 +1,5 @@
1
+ Dir[File.join(File.dirname(__FILE__), 'support/*.rb')].each {|f| require f}
2
+
3
+ RSpec.configure do |config|
4
+ config.extend NodeSpec::RSpecExtensions
5
+ end
@@ -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,4 @@
1
+ shared_context 'initialize with current node', init_with_current_node: true do
2
+ let(:current_node) {double('current node')}
3
+ let(:subject) {described_class.new(current_node)}
4
+ end
@@ -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