vagrant-bolt 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +124 -0
  5. data/.travis.yml +28 -0
  6. data/.yardopts +1 -0
  7. data/Gemfile +37 -0
  8. data/LICENSE +12 -0
  9. data/Puppetfile +7 -0
  10. data/README.md +431 -0
  11. data/Rakefile +19 -0
  12. data/Vagrantfile +47 -0
  13. data/acceptance/artifacts/.keep +0 -0
  14. data/acceptance/components/bolt_spec.rb +98 -0
  15. data/acceptance/skeletons/advanced/Vagrantfile +26 -0
  16. data/acceptance/skeletons/base/Vagrantfile +11 -0
  17. data/acceptance/skeletons/base/modules/facts/CHANGELOG.md +26 -0
  18. data/acceptance/skeletons/base/modules/facts/CONTRIBUTING.md +279 -0
  19. data/acceptance/skeletons/base/modules/facts/Gemfile +98 -0
  20. data/acceptance/skeletons/base/modules/facts/LICENSE +201 -0
  21. data/acceptance/skeletons/base/modules/facts/README.md +45 -0
  22. data/acceptance/skeletons/base/modules/facts/Rakefile +8 -0
  23. data/acceptance/skeletons/base/modules/facts/checksums.json +42 -0
  24. data/acceptance/skeletons/base/modules/facts/lib/puppet/functions/facts/group_by.rb +14 -0
  25. data/acceptance/skeletons/base/modules/facts/metadata.json +62 -0
  26. data/acceptance/skeletons/base/modules/facts/plans/info.pp +16 -0
  27. data/acceptance/skeletons/base/modules/facts/plans/init.pp +13 -0
  28. data/acceptance/skeletons/base/modules/facts/tasks/bash.json +5 -0
  29. data/acceptance/skeletons/base/modules/facts/tasks/bash.sh +93 -0
  30. data/acceptance/skeletons/base/modules/facts/tasks/init.json +10 -0
  31. data/acceptance/skeletons/base/modules/facts/tasks/powershell.json +4 -0
  32. data/acceptance/skeletons/base/modules/facts/tasks/powershell.ps1 +56 -0
  33. data/acceptance/skeletons/base/modules/facts/tasks/ruby.json +4 -0
  34. data/acceptance/skeletons/base/modules/facts/tasks/ruby.rb +40 -0
  35. data/acceptance/skeletons/provisioner/Vagrantfile +19 -0
  36. data/acceptance/skeletons/trigger/Vagrantfile +22 -0
  37. data/acceptance/vagrant-spec.config.rb +22 -0
  38. data/lib/vagrant-bolt.rb +57 -0
  39. data/lib/vagrant-bolt/command.rb +65 -0
  40. data/lib/vagrant-bolt/config.rb +6 -0
  41. data/lib/vagrant-bolt/config/bolt.rb +135 -0
  42. data/lib/vagrant-bolt/config/global.rb +172 -0
  43. data/lib/vagrant-bolt/config_builder.rb +11 -0
  44. data/lib/vagrant-bolt/config_builder/config.rb +150 -0
  45. data/lib/vagrant-bolt/config_builder/monkey_patches.rb +71 -0
  46. data/lib/vagrant-bolt/config_builder/provisioner.rb +106 -0
  47. data/lib/vagrant-bolt/config_builder/triggers.rb +29 -0
  48. data/lib/vagrant-bolt/plugin.rb +39 -0
  49. data/lib/vagrant-bolt/provisioner.rb +18 -0
  50. data/lib/vagrant-bolt/runner.rb +88 -0
  51. data/lib/vagrant-bolt/util/bolt.rb +139 -0
  52. data/lib/vagrant-bolt/util/config.rb +43 -0
  53. data/lib/vagrant-bolt/util/machine.rb +73 -0
  54. data/lib/vagrant-bolt/version.rb +5 -0
  55. data/spec/spec_helper.rb +12 -0
  56. data/spec/unit/config/bolt_spec.rb +150 -0
  57. data/spec/unit/config/global_spec.rb +95 -0
  58. data/spec/unit/provisioner/bolt_spec.rb +39 -0
  59. data/spec/unit/runner/runner_spec.rb +122 -0
  60. data/spec/unit/util/bolt_spec.rb +148 -0
  61. data/spec/unit/util/config_spec.rb +53 -0
  62. data/spec/unit/vagrant_spec.rb +9 -0
  63. data/tasks/acceptance.rake +45 -0
  64. data/tasks/spec.rake +5 -0
  65. data/templates/locales/en.yml +24 -0
  66. data/vagrant-bolt.gemspec +24 -0
  67. metadata +109 -0
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantBolt::Util
4
+ module Config
5
+ # Config Utility Functions
6
+
7
+ # Merge config objects overriding Nil and UNSET_VALUE
8
+ # Since the configs have been finalized they will have `nil` values
9
+ # Arrays will be merged and override parent non arrays
10
+ # instead of UNSET_VALUE
11
+ # @param local [Object] The local config object
12
+ # @param other [Object] The other config object
13
+ # @return [Object] A merged result with local overriding other
14
+ def self.merge_config(local, other)
15
+ result = local.class.new
16
+ [other, local].each do |obj|
17
+ obj.instance_variables.each do |key|
18
+ value = obj.instance_variable_get(key)
19
+ if value.is_a? Array
20
+ res_value = result.instance_variable_get(key)
21
+ value = (value + res_value).uniq if res_value.is_a? Array
22
+ elsif value.is_a? Hash
23
+ res_value = result.instance_variable_get(key)
24
+ value = res_value.merge(value) if res_value.is_a? Hash
25
+ end
26
+ result.instance_variable_set(key, value) if value != Vagrant::Plugin::V2::Config::UNSET_VALUE && !value.nil?
27
+ end
28
+ end
29
+ result.finalize!
30
+ result
31
+ end
32
+
33
+ # Convert a path to the absolute path of the inventory if it is relative
34
+ # @param path [String] The path to convert
35
+ # @param root_path [Object] The root path to append
36
+ # @return [String] The absolute path or nil if path is nil
37
+ def self.full_path(path, root_path)
38
+ return path if path.nil? || root_path.nil?
39
+
40
+ %r{^/.*}.match?(path) ? path : "#{root_path}/#{path}"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantBolt::Util
4
+ module Machine
5
+ # Machine based Utility Functions
6
+
7
+ # Run a command locally with an execute
8
+ # @param command [String] The command to run
9
+ # @param localui [Object] The UI object to write to
10
+ def self.run_command(command, localui)
11
+ localui.info(
12
+ I18n.t('vagrant-bolt.provisioner.bolt.info.running_bolt',
13
+ command: command),
14
+ )
15
+
16
+ # TODO: Update this so it works on windows platforms
17
+ Vagrant::Util::Subprocess.execute(
18
+ 'bash',
19
+ '-c',
20
+ command,
21
+ notify: [:stdout, :stderr],
22
+ env: { PATH: ENV["VAGRANT_OLD_ENV_PATH"] },
23
+ ) do |io_name, data|
24
+ if io_name == :stdout
25
+ localui.info data
26
+ elsif io_name == :stderr
27
+ localui.warn data
28
+ end
29
+ end
30
+ end
31
+
32
+ # Generate a list of active machines in the environment
33
+ # @param env [Object] The Environment
34
+ # @return [Array<Object>]
35
+ def self.nodes_in_environment(env)
36
+ env.active_machines.map { |vm|
37
+ begin
38
+ env.machine(*vm)
39
+ rescue Vagrant::Errors::MachineNotFound
40
+ nil
41
+ end
42
+ }.compact
43
+ end
44
+
45
+ # Return if the guest is windows. This only works for online machines
46
+ # @param machine [Object] The machine
47
+ # @return [Boolean]
48
+ def self.windows?(machine)
49
+ machine.config.vm.communicator == :winrm
50
+ end
51
+
52
+ # Return if the guest is running by checking the communicator
53
+ # @param machine [Object] The machine
54
+ # @return [Boolean]
55
+ def self.running?(machine)
56
+ # Taken from https://github.com/oscar-stack/vagrant-hosts/blob/master/lib/vagrant-hosts/provisioner/hosts.rb
57
+ machine.communicate.ready?
58
+ rescue
59
+ false
60
+ end
61
+
62
+ # Get the running machine object by the machine name
63
+ # @param env [Object] The enviornment to look in
64
+ # @param name [String, Symbol] The name of the machine in the environment
65
+ # @return [Object, nil] The object or nil if it is not found
66
+ def self.machine_by_name(env, name)
67
+ vm = env.active_machines.find { |m| m[0] == name.to_sym }
68
+ env.machine(*vm) unless vm.nil?
69
+ rescue Vagrant::Errors::MachineNotFound
70
+ nil
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantBolt
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
4
+
5
+ # Disable Vagrant autoloading so that other plugins defined in the Gemfile for
6
+ # Acceptance tests are not loaded.
7
+ ENV['VAGRANT_NO_PLUGINS'] = '1'
8
+ ENV['VAGRANT_DISABLE_PLUGIN_INIT'] = '1'
9
+
10
+ require 'vagrant'
11
+ require 'vagrant-spec/unit'
12
+ require 'vagrant-bolt'
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'vagrant-bolt/config'
5
+
6
+ describe VagrantBolt::Config::Bolt do
7
+ let(:machine) { double("machine") }
8
+
9
+ context "validation" do
10
+ it "validates with the defaults" do
11
+ subject.finalize!
12
+ expect(subject.validate(machine)).to eq("Bolt" => [])
13
+ end
14
+
15
+ it "allows for a task" do
16
+ subject.command = :task
17
+ subject.name = "foo"
18
+ subject.finalize!
19
+ expect(subject.validate(machine)).to eq("Bolt" => [])
20
+ end
21
+
22
+ it "allows for a plan" do
23
+ subject.command = :plan
24
+ subject.name = "foo"
25
+ subject.finalize!
26
+ expect(subject.validate(machine)).to eq("Bolt" => [])
27
+ end
28
+
29
+ it "allows for a command" do
30
+ subject.command = :command
31
+ subject.name = "foo"
32
+ subject.finalize!
33
+ expect(subject.validate(machine)).to eq("Bolt" => [])
34
+ end
35
+
36
+ it "reports invalid options" do
37
+ subject.foo = "bar"
38
+ subject.finalize!
39
+ expect(subject.validate(machine)["Bolt"][0]).to eq("The following settings shouldn't exist: foo")
40
+ end
41
+
42
+ it "reports an error when the command is invalid" do
43
+ subject.command = "bar"
44
+ subject.name = "foo"
45
+ subject.finalize!
46
+ expect(subject.validate(machine)["Bolt"][0]).to match(%r{Type can only be})
47
+ end
48
+
49
+ it "reports an error when the name is not specified" do
50
+ subject.command = "task"
51
+ subject.name = nil
52
+ subject.finalize!
53
+ expect(subject.validate(machine)["Bolt"][0]).to match(%r{No name set})
54
+ end
55
+
56
+ it "reports an error when the command is not specified" do
57
+ subject.command = nil
58
+ subject.name = "foo"
59
+ subject.finalize!
60
+ expect(subject.validate(machine)["Bolt"][0]).to match(%r{No command set})
61
+ end
62
+
63
+ it "reports an error when noop is used without a task" do
64
+ subject.command = :plan
65
+ subject.name = "foo"
66
+ subject.noop = true
67
+ subject.finalize!
68
+ expect(subject.validate(machine)["Bolt"][0]).to match(%r{Noop is not compatible})
69
+ end
70
+ end
71
+
72
+ context "defaults" do
73
+ expected_values = {
74
+ nodes: [],
75
+ excludes: [],
76
+ }
77
+ expected_values.each do |val, expected|
78
+ it "defaults #{val} to #{expected}" do
79
+ subject.finalize!
80
+ expect(subject.send(val)).to eq(expected)
81
+ end
82
+ end
83
+
84
+ expected_nil = [
85
+ "name",
86
+ "command",
87
+ "params",
88
+ "node_list",
89
+ "user",
90
+ "password",
91
+ "port",
92
+ "sudo_password",
93
+ "private_key",
94
+ "tmpdir",
95
+ "run_as",
96
+ "args",
97
+ "ssl",
98
+ "ssl_verify",
99
+ "verbose",
100
+ "debug",
101
+ "host_key_check",
102
+ "modulepath",
103
+ "bolt_exe",
104
+ "boltdir",
105
+ "noop",
106
+ ]
107
+ expected_nil.each do |val|
108
+ it "defaults #{val} to nil" do
109
+ subject.finalize!
110
+ expect(subject.send(val)).to eq(nil)
111
+ end
112
+ end
113
+ end
114
+
115
+ context "inventory config" do
116
+ let(:default_hash) do
117
+ {
118
+ "config" => {
119
+ "ssh" => {
120
+ "password" => "foo",
121
+ "port" => "22",
122
+ "run-as" => "root",
123
+ "host-key-check" => false,
124
+ },
125
+ "winrm" => {
126
+ "password" => "foo",
127
+ "port" => "22",
128
+ "run-as" => "root",
129
+ "ssl" => false,
130
+ },
131
+ },
132
+ }
133
+ end
134
+ before(:each) do
135
+ subject.password = 'foo'
136
+ subject.run_as = 'root'
137
+ subject.port = '22'
138
+ subject.ssl = false
139
+ subject.host_key_check = false
140
+ subject.finalize!
141
+ end
142
+
143
+ it 'generates the basic hash structure' do
144
+ expect(subject.inventory_config).to include(default_hash)
145
+ end
146
+ it 'converts names with _ to -' do
147
+ expect(subject.inventory_config['config']['ssh']['run-as']).to eq('root')
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'vagrant-bolt/config'
5
+
6
+ describe VagrantBolt::Config::Global do
7
+ let(:machine) { double("machine") }
8
+
9
+ context "validation" do
10
+ it "validates with the defaults" do
11
+ subject.finalize!
12
+ expect(subject.validate(machine)).to eq("GlobalBolt" => [])
13
+ end
14
+
15
+ it "reports invalid options" do
16
+ subject.foo = "bar"
17
+ subject.finalize!
18
+ expect(subject.validate(machine)["GlobalBolt"][0]).to eq("The following settings shouldn't exist: foo")
19
+ end
20
+ end
21
+
22
+ context "defaults" do
23
+ expected_values = {
24
+ modulepath: "modules",
25
+ bolt_exe: "bolt",
26
+ boltdir: ".",
27
+ }
28
+ expected_values.each do |val, expected|
29
+ it "defaults #{val} to #{expected}" do
30
+ subject.finalize!
31
+ expect(subject.send(val)).to eq(expected)
32
+ end
33
+ end
34
+
35
+ expected_nil = [
36
+ "user",
37
+ "password",
38
+ "port",
39
+ "sudo_password",
40
+ "private_key",
41
+ "tmpdir",
42
+ "run_as",
43
+ "ssl",
44
+ "ssl_verify",
45
+ "host_key_check",
46
+ "verbose",
47
+ "debug",
48
+ "facts",
49
+ "vars",
50
+ "features",
51
+ ]
52
+ expected_nil.each do |val|
53
+ it "defaults #{val} to nil" do
54
+ subject.finalize!
55
+ expect(subject.send(val)).to eq(nil)
56
+ end
57
+ end
58
+ end
59
+
60
+ context "inventory config" do
61
+ let(:default_hash) do
62
+ {
63
+ "config" => {
64
+ "ssh" => {
65
+ "password" => "foo",
66
+ "port" => "22",
67
+ "run-as" => "root",
68
+ "host-key-check" => false,
69
+ },
70
+ "winrm" => {
71
+ "password" => "foo",
72
+ "port" => "22",
73
+ "run-as" => "root",
74
+ "ssl" => false,
75
+ },
76
+ },
77
+ }
78
+ end
79
+ before(:each) do
80
+ subject.password = 'foo'
81
+ subject.run_as = 'root'
82
+ subject.port = '22'
83
+ subject.ssl = false
84
+ subject.host_key_check = false
85
+ subject.finalize!
86
+ end
87
+
88
+ it 'generates the basic hash structure' do
89
+ expect(subject.inventory_config).to include(default_hash)
90
+ end
91
+ it 'converts names with _ to -' do
92
+ expect(subject.inventory_config['config']['ssh']['run-as']).to eq('root')
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'vagrant-bolt/config'
5
+ require 'vagrant-bolt/provisioner'
6
+
7
+ describe VagrantBolt::Provisioner do
8
+ include_context 'vagrant-unit'
9
+ subject { described_class.new(machine, config) }
10
+
11
+ let(:iso_env) do
12
+ env = isolated_environment
13
+ env.vagrantfile <<-VAGRANTFILE
14
+ Vagrant.configure('2') do |config|
15
+ config.vm.define :server
16
+ end
17
+ VAGRANTFILE
18
+ env.create_vagrant_env
19
+ end
20
+ let(:machine) { iso_env.machine(:server, :dummy) }
21
+ let(:config) { double :config }
22
+ let(:runner) { double :runner }
23
+ before(:each) do
24
+ allow(machine).to receive(:env).and_return(:iso_env)
25
+ allow(config).to receive(:name).and_return('foo')
26
+ allow(config).to receive(:command).and_return('task')
27
+ end
28
+
29
+ context 'provision' do
30
+ before(:each) do
31
+ allow(VagrantBolt::Runner).to receive(:new).with(:iso_env, machine, config).and_return(runner)
32
+ allow(runner).to receive(:run).with('task', 'foo').and_return('runner created')
33
+ end
34
+
35
+ it 'creates a new runner' do
36
+ expect(subject.provision).to eq('runner created')
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'vagrant-bolt/runner'
5
+ require 'vagrant-bolt/config'
6
+
7
+ describe VagrantBolt::Runner do
8
+ include_context 'vagrant-unit'
9
+ subject { described_class.new(iso_env, machine, config) }
10
+
11
+ let(:iso_env) do
12
+ env = isolated_environment
13
+ env.vagrantfile <<-VAGRANTFILE
14
+ Vagrant.configure('2') do |config|
15
+ config.vm.define :server
16
+ config.vm.define :server2
17
+ end
18
+ VAGRANTFILE
19
+ env.create_vagrant_env
20
+ end
21
+ let(:machine) { iso_env.machine(:server, :dummy) }
22
+ let(:machine2) { iso_env.machine(:server2, :dummy) }
23
+ let(:runner) { double :runner }
24
+ let(:config) { VagrantBolt::Config::Bolt.new }
25
+ let(:subprocess_result) do
26
+ double("subprocess_result").tap do |result|
27
+ allow(result).to receive(:exit_code).and_return(0)
28
+ allow(result).to receive(:stderr).and_return("")
29
+ end
30
+ end
31
+ let(:root_path) { '/root/path' }
32
+ let(:local_data_path) { '/local/data/path' }
33
+ let(:inventory_path) { "#{local_data_path}/bolt_inventory.yaml" }
34
+ before(:each) do
35
+ allow(VagrantBolt::Util::Bolt).to receive(:update_inventory_file).with(iso_env).and_return(inventory_path)
36
+ allow(iso_env).to receive(:root_path).and_return(root_path)
37
+ allow(iso_env).to receive(:local_data_path).and_return(local_data_path)
38
+ allow(machine).to receive(:env).and_return(:iso_env)
39
+ allow(machine).to receive(:ssh_info).and_return(
40
+ host: 'foo',
41
+ port: '22',
42
+ username: 'user',
43
+ private_key_path: ['path'],
44
+ verify_host_key: true,
45
+ )
46
+ config.finalize!
47
+ end
48
+
49
+ context 'setup_overrides' do
50
+ before(:each) do
51
+ allow(VagrantBolt::Util::Machine).to receive(:nodes_in_environment).with(iso_env).and_return([machine, machine2])
52
+ end
53
+ it 'adds the command and name to the config' do
54
+ result = subject.send(:setup_overrides, 'task', 'foo')
55
+ expect(result.command).to eq('task')
56
+ expect(result.name).to eq('foo')
57
+ end
58
+
59
+ it 'uses the server name for the nodes' do
60
+ result = subject.send(:setup_overrides, 'task', 'foo')
61
+ expect(result.node_list).to eq('server')
62
+ end
63
+
64
+ it 'allows for using multiple nodes' do
65
+ config.nodes = ['server', 'server2']
66
+ config.finalize!
67
+ result = subject.send(:setup_overrides, 'task', 'foo')
68
+ expect(result.node_list).to eq('server,server2')
69
+ end
70
+
71
+ it 'adds all nodes when "all" is specified' do
72
+ config.nodes = 'all'
73
+ config.finalize!
74
+ result = subject.send(:setup_overrides, 'task', 'foo')
75
+ expect(result.node_list).to eq('server,server2')
76
+ end
77
+
78
+ it 'does not override specified ssh settings' do
79
+ config.node_list = 'ssh://test:22'
80
+ config.user = 'root'
81
+ config.finalize!
82
+ result = subject.send(:setup_overrides, 'task', 'foo')
83
+ expect(result.node_list).to eq('ssh://test:22')
84
+ expect(result.user).to eq('root')
85
+ end
86
+
87
+ it 'allows for specifying additional args' do
88
+ result = subject.send(:setup_overrides, 'task', 'foo', password: 'foo')
89
+ expect(result.password).to eq('foo')
90
+ end
91
+ end
92
+
93
+ context 'run' do
94
+ let(:options) { { notify: [:stdout, :stderr], env: { PATH: nil } } }
95
+ before(:each) do
96
+ allow(Vagrant::Util::Subprocess).to receive(:execute).and_return(subprocess_result)
97
+ end
98
+
99
+ it 'does not raise an exeption when all parameters are specified' do
100
+ expect { subject.run('task', 'foo') }.to_not raise_error
101
+ end
102
+
103
+ it 'raises an exception if the command is not specified' do
104
+ expect { subject.run(nil, 'foo') }.to raise_error(Vagrant::Errors::ConfigInvalid, %r{No command set})
105
+ end
106
+
107
+ it 'raises an exception if the name is not specified' do
108
+ expect { subject.run('task', nil) }.to raise_error(Vagrant::Errors::ConfigInvalid, %r{No name set})
109
+ end
110
+
111
+ it 'creates a shell execution' do
112
+ config.bolt_exe = 'bolt'
113
+ config.modulepath = 'modules'
114
+ config.boltdir = '.'
115
+ config.node_list = 'ssh://test:22'
116
+ config.finalize!
117
+ command = "bolt task run 'foo' --boltdir '#{root_path}/.' --modulepath '#{root_path}/modules' --inventoryfile '#{inventory_path}' --nodes 'ssh://test:22'"
118
+ expect(Vagrant::Util::Subprocess).to receive(:execute).with('bash', '-c', command, options).and_return(subprocess_result)
119
+ subject.run('task', 'foo')
120
+ end
121
+ end
122
+ end