vagrant-bolt 0.1.0

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 (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