vagrant-ansible-fixed 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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/Gemfile +9 -0
- data/README.md +37 -0
- data/Rakefile +3 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/cap/guest/arch/ansible_install.rb +19 -0
- data/lib/cap/guest/debian/ansible_install.rb +28 -0
- data/lib/cap/guest/fedora/ansible_install.rb +26 -0
- data/lib/cap/guest/freebsd/ansible_install.rb +18 -0
- data/lib/cap/guest/posix/ansible_installed.rb +25 -0
- data/lib/cap/guest/redhat/ansible_install.rb +27 -0
- data/lib/cap/guest/suse/ansible_install.rb +18 -0
- data/lib/cap/guest/ubuntu/ansible_install.rb +22 -0
- data/lib/config/base.rb +119 -0
- data/lib/config/guest.rb +69 -0
- data/lib/config/host.rb +64 -0
- data/lib/errors.rb +27 -0
- data/lib/helpers.rb +43 -0
- data/lib/plugin.rb +80 -0
- data/lib/provisioner/base.rb +219 -0
- data/lib/provisioner/guest.rb +149 -0
- data/lib/provisioner/host.rb +257 -0
- data/lib/version.rb +5 -0
- data/vagrant-ansible-fixed.gemspec +23 -0
- metadata +96 -0
data/lib/plugin.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require "vagrant"
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Ansible_Fixed
|
5
|
+
class Plugin < Vagrant.plugin("2")
|
6
|
+
|
7
|
+
name "ansible_fixed"
|
8
|
+
description <<-DESC
|
9
|
+
Provides support for provisioning your virtual machines with Ansible
|
10
|
+
from the Vagrant host (`ansible`) or from the guests (`ansible_local`).
|
11
|
+
DESC with patch for Windows systems until version 1.8.2 is public
|
12
|
+
|
13
|
+
config("ansible_fixed", :provisioner) do
|
14
|
+
require_relative "config/host"
|
15
|
+
Config::Host
|
16
|
+
end
|
17
|
+
|
18
|
+
config("ansible_fixed_local", :provisioner) do
|
19
|
+
require_relative "config/guest"
|
20
|
+
Config::Guest
|
21
|
+
end
|
22
|
+
|
23
|
+
provisioner("ansible_fixed") do
|
24
|
+
require_relative "provisioner/host"
|
25
|
+
Provisioner::Host
|
26
|
+
end
|
27
|
+
|
28
|
+
provisioner("ansible_fixed_local") do
|
29
|
+
require_relative "provisioner/guest"
|
30
|
+
Provisioner::Guest
|
31
|
+
end
|
32
|
+
|
33
|
+
guest_capability(:linux, :ansible_installed) do
|
34
|
+
require_relative "cap/guest/posix/ansible_installed"
|
35
|
+
Cap::Guest::POSIX::AnsibleInstalled
|
36
|
+
end
|
37
|
+
|
38
|
+
guest_capability(:freebsd, :ansible_installed) do
|
39
|
+
require_relative "cap/guest/posix/ansible_installed"
|
40
|
+
Cap::Guest::POSIX::AnsibleInstalled
|
41
|
+
end
|
42
|
+
|
43
|
+
guest_capability(:arch, :ansible_install) do
|
44
|
+
require_relative "cap/guest/arch/ansible_install"
|
45
|
+
Cap::Guest::Arch::AnsibleInstall
|
46
|
+
end
|
47
|
+
|
48
|
+
guest_capability(:debian, :ansible_install) do
|
49
|
+
require_relative "cap/guest/debian/ansible_install"
|
50
|
+
Cap::Guest::Debian::AnsibleInstall
|
51
|
+
end
|
52
|
+
|
53
|
+
guest_capability(:ubuntu, :ansible_install) do
|
54
|
+
require_relative "cap/guest/ubuntu/ansible_install"
|
55
|
+
Cap::Guest::Ubuntu::AnsibleInstall
|
56
|
+
end
|
57
|
+
|
58
|
+
guest_capability(:fedora, :ansible_install) do
|
59
|
+
require_relative "cap/guest/fedora/ansible_install"
|
60
|
+
Cap::Guest::Fedora::AnsibleInstall
|
61
|
+
end
|
62
|
+
|
63
|
+
guest_capability(:redhat, :ansible_install) do
|
64
|
+
require_relative "cap/guest/redhat/ansible_install"
|
65
|
+
Cap::Guest::RedHat::AnsibleInstall
|
66
|
+
end
|
67
|
+
|
68
|
+
guest_capability(:suse, :ansible_install) do
|
69
|
+
require_relative "cap/guest/suse/ansible_install"
|
70
|
+
Cap::Guest::SUSE::AnsibleInstall
|
71
|
+
end
|
72
|
+
|
73
|
+
guest_capability(:freebsd, :ansible_install) do
|
74
|
+
require_relative "cap/guest/freebsd/ansible_install"
|
75
|
+
Cap::Guest::FreeBSD::AnsibleInstall
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require_relative "../errors"
|
2
|
+
require_relative "../helpers"
|
3
|
+
|
4
|
+
module VagrantPlugins
|
5
|
+
module Ansible_Fixed
|
6
|
+
module Provisioner
|
7
|
+
|
8
|
+
# This class is a base class where the common functionality shared between
|
9
|
+
# both Ansible provisioners are stored.
|
10
|
+
# This is **not an actual provisioner**.
|
11
|
+
# Instead, {Host} (ansible) or {Guest} (ansible_local) should be used.
|
12
|
+
|
13
|
+
class Base < Vagrant.plugin("2", :provisioner)
|
14
|
+
|
15
|
+
RANGE_PATTERN = %r{(?:\[[a-z]:[a-z]\]|\[[0-9]+?:[0-9]+?\])}.freeze
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def initialize(machine, config)
|
20
|
+
super
|
21
|
+
|
22
|
+
@command_arguments = []
|
23
|
+
@environment_variables = {}
|
24
|
+
@inventory_machines = {}
|
25
|
+
@inventory_path = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def prepare_common_command_arguments
|
29
|
+
# By default we limit by the current machine,
|
30
|
+
# but this can be overridden by the `limit` option.
|
31
|
+
if config.limit
|
32
|
+
@command_arguments << "--limit=#{Helpers::as_list_argument(config.limit)}"
|
33
|
+
else
|
34
|
+
@command_arguments << "--limit=#{@machine.name}"
|
35
|
+
end
|
36
|
+
|
37
|
+
@command_arguments << "--inventory-file=#{inventory_path}"
|
38
|
+
@command_arguments << "--extra-vars=#{extra_vars_argument}" if config.extra_vars
|
39
|
+
@command_arguments << "--sudo" if config.sudo
|
40
|
+
@command_arguments << "--sudo-user=#{config.sudo_user}" if config.sudo_user
|
41
|
+
@command_arguments << "#{verbosity_argument}" if verbosity_is_enabled?
|
42
|
+
@command_arguments << "--vault-password-file=#{config.vault_password_file}" if config.vault_password_file
|
43
|
+
@command_arguments << "--tags=#{Helpers::as_list_argument(config.tags)}" if config.tags
|
44
|
+
@command_arguments << "--skip-tags=#{Helpers::as_list_argument(config.skip_tags)}" if config.skip_tags
|
45
|
+
@command_arguments << "--start-at-task=#{config.start_at_task}" if config.start_at_task
|
46
|
+
|
47
|
+
# Finally, add the raw configuration options, which has the highest precedence
|
48
|
+
# and can therefore potentially override any other options of this provisioner.
|
49
|
+
@command_arguments.concat(Helpers::as_array(config.raw_arguments)) if config.raw_arguments
|
50
|
+
end
|
51
|
+
|
52
|
+
def prepare_common_environment_variables
|
53
|
+
# Ensure Ansible output isn't buffered so that we receive output
|
54
|
+
# on a task-by-task basis.
|
55
|
+
@environment_variables["PYTHONUNBUFFERED"] = 1
|
56
|
+
|
57
|
+
# When Ansible output is piped in Vagrant integration, its default colorization is
|
58
|
+
# automatically disabled and the only way to re-enable colors is to use ANSIBLE_FORCE_COLOR.
|
59
|
+
@environment_variables["ANSIBLE_FORCE_COLOR"] = "true" if @machine.env.ui.color?
|
60
|
+
# Setting ANSIBLE_NOCOLOR is "unnecessary" at the moment, but this could change in the future
|
61
|
+
# (e.g. local provisioner [GH-2103], possible change in vagrant/ansible integration, etc.)
|
62
|
+
@environment_variables["ANSIBLE_NOCOLOR"] = "true" if !@machine.env.ui.color?
|
63
|
+
end
|
64
|
+
|
65
|
+
# Auto-generate "safe" inventory file based on Vagrantfile,
|
66
|
+
# unless inventory_path is explicitly provided
|
67
|
+
def inventory_path
|
68
|
+
if config.inventory_path
|
69
|
+
config.inventory_path
|
70
|
+
else
|
71
|
+
@inventory_path ||= generate_inventory
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_inventory_host_vars_string(machine_name)
|
76
|
+
# In Ruby, Symbol and String values are different, but
|
77
|
+
# Vagrant has to unify them for better user experience.
|
78
|
+
vars = config.host_vars[machine_name.to_sym]
|
79
|
+
if !vars
|
80
|
+
vars = config.host_vars[machine_name.to_s]
|
81
|
+
end
|
82
|
+
s = nil
|
83
|
+
if vars.is_a?(Hash)
|
84
|
+
s = vars.each.collect{ |k, v| "#{k}=#{v}" }.join(" ")
|
85
|
+
elsif vars.is_a?(Array)
|
86
|
+
s = vars.join(" ")
|
87
|
+
elsif vars.is_a?(String)
|
88
|
+
s = vars
|
89
|
+
end
|
90
|
+
if s and !s.empty? then s else nil end
|
91
|
+
end
|
92
|
+
|
93
|
+
def generate_inventory
|
94
|
+
inventory = "# Generated by Vagrant\n\n"
|
95
|
+
|
96
|
+
# This "abstract" step must fill the @inventory_machines list
|
97
|
+
# and return the list of supported host(s)
|
98
|
+
inventory += generate_inventory_machines
|
99
|
+
|
100
|
+
inventory += generate_inventory_groups
|
101
|
+
|
102
|
+
# This "abstract" step must create the inventory file and
|
103
|
+
# return its location path
|
104
|
+
# TODO: explain possible race conditions, etc.
|
105
|
+
@inventory_path = ship_generated_inventory(inventory)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Write out groups information.
|
109
|
+
# All defined groups will be included, but only supported
|
110
|
+
# machines and defined child groups will be included.
|
111
|
+
def generate_inventory_groups
|
112
|
+
groups_of_groups = {}
|
113
|
+
defined_groups = []
|
114
|
+
group_vars = {}
|
115
|
+
inventory_groups = ""
|
116
|
+
|
117
|
+
# Verify if host range patterns exist and warn
|
118
|
+
if config.groups.any? { |gm| gm.to_s[RANGE_PATTERN] }
|
119
|
+
@machine.ui.warn(I18n.t("vagrant.provisioners.ansible.ansible_host_pattern_detected"))
|
120
|
+
end
|
121
|
+
|
122
|
+
config.groups.each_pair do |gname, gmembers|
|
123
|
+
if gname.is_a?(Symbol)
|
124
|
+
gname = gname.to_s
|
125
|
+
end
|
126
|
+
|
127
|
+
if gmembers.is_a?(String)
|
128
|
+
gmembers = gmembers.split(/\s+/)
|
129
|
+
elsif gmembers.is_a?(Hash)
|
130
|
+
gmembers = gmembers.each.collect{ |k, v| "#{k}=#{v}" }
|
131
|
+
elsif !gmembers.is_a?(Array)
|
132
|
+
gmembers = []
|
133
|
+
end
|
134
|
+
|
135
|
+
if gname.end_with?(":children")
|
136
|
+
groups_of_groups[gname] = gmembers
|
137
|
+
defined_groups << gname.sub(/:children$/, '')
|
138
|
+
elsif gname.end_with?(":vars")
|
139
|
+
group_vars[gname] = gmembers
|
140
|
+
else
|
141
|
+
defined_groups << gname
|
142
|
+
inventory_groups += "\n[#{gname}]\n"
|
143
|
+
gmembers.each do |gm|
|
144
|
+
# TODO : Expand and validate host range patterns
|
145
|
+
# against @inventory_machines list before adding them
|
146
|
+
# otherwise abort with an error message
|
147
|
+
if gm[RANGE_PATTERN]
|
148
|
+
inventory_groups += "#{gm}\n"
|
149
|
+
end
|
150
|
+
inventory_groups += "#{gm}\n" if @inventory_machines.include?(gm.to_sym)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
defined_groups.uniq!
|
156
|
+
groups_of_groups.each_pair do |gname, gmembers|
|
157
|
+
inventory_groups += "\n[#{gname}]\n"
|
158
|
+
gmembers.each do |gm|
|
159
|
+
inventory_groups += "#{gm}\n" if defined_groups.include?(gm)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
group_vars.each_pair do |gname, gmembers|
|
164
|
+
if defined_groups.include?(gname.sub(/:vars$/, ""))
|
165
|
+
inventory_groups += "\n[#{gname}]\n" + gmembers.join("\n") + "\n"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
return inventory_groups
|
170
|
+
end
|
171
|
+
|
172
|
+
def extra_vars_argument
|
173
|
+
if config.extra_vars.kind_of?(String) and config.extra_vars =~ /^@.+$/
|
174
|
+
# A JSON or YAML file is referenced.
|
175
|
+
config.extra_vars
|
176
|
+
else
|
177
|
+
# Expected to be a Hash after config validation.
|
178
|
+
config.extra_vars.to_json
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def get_galaxy_role_file(base_dir)
|
183
|
+
Helpers::expand_path_in_unix_style(config.galaxy_role_file, base_dir)
|
184
|
+
end
|
185
|
+
|
186
|
+
def get_galaxy_roles_path(base_dir)
|
187
|
+
if config.galaxy_roles_path
|
188
|
+
Helpers::expand_path_in_unix_style(config.galaxy_roles_path, base_dir)
|
189
|
+
else
|
190
|
+
playbook_path = Helpers::expand_path_in_unix_style(config.playbook, base_dir)
|
191
|
+
File.join(Pathname.new(playbook_path).parent, 'roles')
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def ui_running_ansible_command(name, command)
|
196
|
+
@machine.ui.detail I18n.t("vagrant.provisioners.ansible.running_#{name}")
|
197
|
+
if verbosity_is_enabled?
|
198
|
+
# Show the ansible command in use
|
199
|
+
@machine.env.ui.detail command
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def verbosity_is_enabled?
|
204
|
+
config.verbose && !config.verbose.to_s.empty?
|
205
|
+
end
|
206
|
+
|
207
|
+
def verbosity_argument
|
208
|
+
if config.verbose.to_s =~ /^-?(v+)$/
|
209
|
+
"-#{$+}"
|
210
|
+
else
|
211
|
+
# safe default, in case input strays
|
212
|
+
'-v'
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
|
5
|
+
module VagrantPlugins
|
6
|
+
module Ansible_Fixed
|
7
|
+
module Provisioner
|
8
|
+
class Guest < Base
|
9
|
+
|
10
|
+
def initialize(machine, config)
|
11
|
+
super
|
12
|
+
@logger = Log4r::Logger.new("vagrant::provisioners::ansible_guest")
|
13
|
+
end
|
14
|
+
|
15
|
+
def provision
|
16
|
+
check_and_install_ansible
|
17
|
+
execute_ansible_galaxy_on_guest if config.galaxy_role_file
|
18
|
+
execute_ansible_playbook_on_guest
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
#
|
24
|
+
# This handles verifying the Ansible installation, installing it if it was
|
25
|
+
# requested, and so on. This method will raise exceptions if things are wrong.
|
26
|
+
#
|
27
|
+
# Current limitations:
|
28
|
+
# - The installation of a specific Ansible version is not supported.
|
29
|
+
# Such feature is difficult to systematically provide via package repositories (apt, yum, ...).
|
30
|
+
# Installing via pip python packaging or directly from github source would be appropriate,
|
31
|
+
# but these approaches require more dependency burden.
|
32
|
+
# - There is no guarantee that the automated installation will replace
|
33
|
+
# a previous Ansible installation.
|
34
|
+
#
|
35
|
+
def check_and_install_ansible
|
36
|
+
@logger.info("Checking for Ansible installation...")
|
37
|
+
|
38
|
+
# If the guest cannot check if Ansible is installed,
|
39
|
+
# print a warning and try to continue without any installation attempt...
|
40
|
+
if !@machine.guest.capability?(:ansible_installed)
|
41
|
+
@machine.ui.warn(I18n.t("vagrant.provisioners.ansible.cannot_detect"))
|
42
|
+
return
|
43
|
+
end
|
44
|
+
|
45
|
+
# Try to install Ansible (if needed and requested)
|
46
|
+
if config.install &&
|
47
|
+
(config.version.to_s.to_sym == :latest ||
|
48
|
+
!@machine.guest.capability(:ansible_installed, config.version))
|
49
|
+
@machine.ui.detail I18n.t("vagrant.provisioners.ansible.installing")
|
50
|
+
@machine.guest.capability(:ansible_install)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Check that ansible binaries are well installed on the guest,
|
54
|
+
@machine.communicate.execute(
|
55
|
+
"ansible-galaxy info --help && ansible-playbook --help",
|
56
|
+
:error_class => Ansible::Errors::AnsibleNotFoundOnGuest,
|
57
|
+
:error_key => :ansible_not_found_on_guest)
|
58
|
+
|
59
|
+
# Check if requested ansible version is available
|
60
|
+
if (!config.version.empty? &&
|
61
|
+
config.version.to_s.to_sym != :latest &&
|
62
|
+
!@machine.guest.capability(:ansible_installed, config.version))
|
63
|
+
raise Ansible::Errors::AnsibleVersionNotFoundOnGuest, required_version: config.version.to_s
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def execute_ansible_galaxy_on_guest
|
68
|
+
command_values = {
|
69
|
+
:role_file => get_galaxy_role_file(config.provisioning_path),
|
70
|
+
:roles_path => get_galaxy_roles_path(config.provisioning_path)
|
71
|
+
}
|
72
|
+
remote_command = config.galaxy_command % command_values
|
73
|
+
|
74
|
+
execute_ansible_command_on_guest "galaxy", remote_command
|
75
|
+
end
|
76
|
+
|
77
|
+
def execute_ansible_playbook_on_guest
|
78
|
+
prepare_common_command_arguments
|
79
|
+
prepare_common_environment_variables
|
80
|
+
|
81
|
+
command = (%w(ansible-playbook) << @command_arguments << config.playbook).flatten
|
82
|
+
remote_command = "cd #{config.provisioning_path} && #{Helpers::stringify_ansible_playbook_command(@environment_variables, command)}"
|
83
|
+
|
84
|
+
execute_ansible_command_on_guest "playbook", remote_command
|
85
|
+
end
|
86
|
+
|
87
|
+
def execute_ansible_command_on_guest(name, command)
|
88
|
+
ui_running_ansible_command name, command
|
89
|
+
|
90
|
+
result = execute_on_guest(command)
|
91
|
+
raise Ansible::Errors::AnsibleCommandFailed if result != 0
|
92
|
+
end
|
93
|
+
|
94
|
+
def execute_on_guest(command)
|
95
|
+
@machine.communicate.execute(command, :error_check => false) do |type, data|
|
96
|
+
if [:stderr, :stdout].include?(type)
|
97
|
+
@machine.env.ui.info(data, :new_line => false, :prefix => false)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def ship_generated_inventory(inventory_content)
|
103
|
+
inventory_basedir = File.join(config.tmp_path, "inventory")
|
104
|
+
inventory_path = File.join(inventory_basedir, "vagrant_ansible_local_inventory")
|
105
|
+
|
106
|
+
temp_inventory = Tempfile.new("vagrant_ansible_local_inventory_#{@machine.name}")
|
107
|
+
temp_inventory.write(inventory_content)
|
108
|
+
temp_inventory.close
|
109
|
+
|
110
|
+
create_and_chown_remote_folder(inventory_basedir)
|
111
|
+
@machine.communicate.tap do |comm|
|
112
|
+
comm.sudo("rm -f #{inventory_path}", error_check: false)
|
113
|
+
comm.upload(temp_inventory.path, inventory_path)
|
114
|
+
end
|
115
|
+
|
116
|
+
return inventory_basedir
|
117
|
+
end
|
118
|
+
|
119
|
+
def generate_inventory_machines
|
120
|
+
machines = ""
|
121
|
+
|
122
|
+
# TODO: Instead, why not loop over active_machines and skip missing guests, like in Host?
|
123
|
+
machine.env.machine_names.each do |machine_name|
|
124
|
+
begin
|
125
|
+
@inventory_machines[machine_name] = machine_name
|
126
|
+
if @machine.name == machine_name
|
127
|
+
machines += "#{machine_name} ansible_connection=local\n"
|
128
|
+
else
|
129
|
+
machines += "#{machine_name}\n"
|
130
|
+
end
|
131
|
+
host_vars = get_inventory_host_vars_string(machine_name)
|
132
|
+
machines.sub!(/\n$/, " #{host_vars}\n") if host_vars
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
return machines
|
137
|
+
end
|
138
|
+
|
139
|
+
def create_and_chown_remote_folder(path)
|
140
|
+
@machine.communicate.tap do |comm|
|
141
|
+
comm.sudo("mkdir -p #{path}")
|
142
|
+
comm.sudo("chown -h #{@machine.ssh_info[:username]} #{path}")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|