Merge pull request #4761 from mitchellh/sethvargo/chef_omnibus
Automatically install Chef when provisioning with Chef
This commit is contained in:
commit
1c06da04e6
|
@ -48,3 +48,4 @@ website/docs/Rakefile
|
||||||
website/www/.sass-cache
|
website/www/.sass-cache
|
||||||
website/www/build
|
website/www/build
|
||||||
website/www/Rakefile
|
website/www/Rakefile
|
||||||
|
exec/
|
||||||
|
|
|
@ -32,6 +32,7 @@ IMPROVEMENTS:
|
||||||
more easily. Vagrant will login for you if you specify auth. [GH-4042]
|
more easily. Vagrant will login for you if you specify auth. [GH-4042]
|
||||||
- providers/docker: `stop_timeout` can be used to modify the `docker stop`
|
- providers/docker: `stop_timeout` can be used to modify the `docker stop`
|
||||||
timeout. [GH-4504]
|
timeout. [GH-4504]
|
||||||
|
- provisioners/chef: Automatically install Chef when using a Chef provisioner.
|
||||||
- synced\_folders/nfs: Won't use `sudo` to write to /etc/exports if there
|
- synced\_folders/nfs: Won't use `sudo` to write to /etc/exports if there
|
||||||
are write privileges. [GH-2643]
|
are write privileges. [GH-2643]
|
||||||
- synced\_folders/smb: Credentials from one SMB will be copied to the rest. [GH-4675]
|
- synced\_folders/smb: Credentials from one SMB will be copied to the rest. [GH-4675]
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
require_relative "../../omnibus"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module Chef
|
||||||
|
module Cap
|
||||||
|
module Debian
|
||||||
|
module ChefInstall
|
||||||
|
def self.chef_install(machine, version, prerelease)
|
||||||
|
machine.communicate.sudo("apt-get update -y -qq")
|
||||||
|
machine.communicate.sudo("apt-get install -y -qq curl")
|
||||||
|
|
||||||
|
command = Omnibus.build_command(version, prerelease)
|
||||||
|
machine.communicate.sudo(command)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,22 @@
|
||||||
|
module VagrantPlugins
|
||||||
|
module Chef
|
||||||
|
module Cap
|
||||||
|
module Linux
|
||||||
|
module ChefInstalled
|
||||||
|
# Check if Chef is installed at the given version.
|
||||||
|
# @return [true, false]
|
||||||
|
def self.chef_installed(machine, version)
|
||||||
|
knife = "/opt/chef/bin/knife"
|
||||||
|
command = "test -x #{knife}"
|
||||||
|
|
||||||
|
if version != :latest
|
||||||
|
command << "&& #{knife} --version | grep 'Chef: #{version}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
machine.communicate.test(command, sudo: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,18 @@
|
||||||
|
require_relative "../../omnibus"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module Chef
|
||||||
|
module Cap
|
||||||
|
module Redhat
|
||||||
|
module ChefInstall
|
||||||
|
def self.chef_install(machine, version, prerelease)
|
||||||
|
machine.communicate.sudo("yum install -y -q curl")
|
||||||
|
|
||||||
|
command = Omnibus.build_command(version, prerelease)
|
||||||
|
machine.communicate.sudo(command)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,134 +6,98 @@ module VagrantPlugins
|
||||||
class Base < Vagrant.plugin("2", :config)
|
class Base < Vagrant.plugin("2", :config)
|
||||||
extend Vagrant::Util::Counter
|
extend Vagrant::Util::Counter
|
||||||
|
|
||||||
attr_accessor :arguments
|
# The path to Chef's bin/ directory.
|
||||||
attr_accessor :attempts
|
# @return [String]
|
||||||
attr_accessor :binary_path
|
attr_accessor :binary_path
|
||||||
|
|
||||||
|
# Arbitrary environment variables to set before running the Chef
|
||||||
|
# provisioner command.
|
||||||
|
# @return [String]
|
||||||
attr_accessor :binary_env
|
attr_accessor :binary_env
|
||||||
attr_accessor :custom_config_path
|
|
||||||
attr_accessor :encrypted_data_bag_secret_key_path
|
# Install Chef on the system if it does not exist. Default is true.
|
||||||
attr_accessor :environment
|
# This is a trinary attribute (it can have three values):
|
||||||
attr_accessor :formatter
|
#
|
||||||
attr_accessor :http_proxy
|
# - true (bool) install Chef
|
||||||
attr_accessor :http_proxy_user
|
# - false (bool) do not install Chef
|
||||||
attr_accessor :http_proxy_pass
|
# - "force" (string) install Chef, even if it is already installed at
|
||||||
attr_accessor :https_proxy
|
# the proper version
|
||||||
attr_accessor :https_proxy_user
|
#
|
||||||
attr_accessor :https_proxy_pass
|
# @return [true, false, String]
|
||||||
attr_accessor :json
|
attr_accessor :install
|
||||||
|
|
||||||
|
# The Chef log level. See the Chef docs for acceptable values.
|
||||||
|
# @return [String, Symbol]
|
||||||
attr_accessor :log_level
|
attr_accessor :log_level
|
||||||
attr_accessor :no_proxy
|
|
||||||
attr_accessor :node_name
|
# Install a prerelease version of Chef.
|
||||||
attr_accessor :provisioning_path
|
# @return [true, false]
|
||||||
attr_accessor :run_list
|
attr_accessor :prerelease
|
||||||
attr_accessor :file_cache_path
|
|
||||||
attr_accessor :file_backup_path
|
# The version of Chef to install. If Chef is already installed on the
|
||||||
attr_accessor :verbose_logging
|
# system, the installed version is compared with the requested version.
|
||||||
|
# If they match, no action is taken. If they do not match, version of
|
||||||
|
# the value specified in this attribute will be installed over top of
|
||||||
|
# the existing version (a warning will be displayed).
|
||||||
|
#
|
||||||
|
# You can also specify "latest" (default), which will install the latest
|
||||||
|
# version of Chef on the system. In this case, Chef will use whatever
|
||||||
|
# version is on the system. To force the newest version of Chef to be
|
||||||
|
# installed on every provision, set the {#install} option to "force".
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :version
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super
|
super
|
||||||
|
|
||||||
@arguments = UNSET_VALUE
|
@binary_path = UNSET_VALUE
|
||||||
@attempts = UNSET_VALUE
|
@binary_env = UNSET_VALUE
|
||||||
@binary_path = UNSET_VALUE
|
@install = UNSET_VALUE
|
||||||
@binary_env = UNSET_VALUE
|
@log_level = UNSET_VALUE
|
||||||
@custom_config_path = UNSET_VALUE
|
@prerelease = UNSET_VALUE
|
||||||
@encrypted_data_bag_secret_key_path = UNSET_VALUE
|
@version = UNSET_VALUE
|
||||||
@environment = UNSET_VALUE
|
|
||||||
@formatter = UNSET_VALUE
|
|
||||||
@http_proxy = UNSET_VALUE
|
|
||||||
@http_proxy_user = UNSET_VALUE
|
|
||||||
@http_proxy_pass = UNSET_VALUE
|
|
||||||
@https_proxy = UNSET_VALUE
|
|
||||||
@https_proxy_user = UNSET_VALUE
|
|
||||||
@https_proxy_pass = UNSET_VALUE
|
|
||||||
@log_level = UNSET_VALUE
|
|
||||||
@no_proxy = UNSET_VALUE
|
|
||||||
@node_name = UNSET_VALUE
|
|
||||||
@provisioning_path = UNSET_VALUE
|
|
||||||
@file_cache_path = UNSET_VALUE
|
|
||||||
@file_backup_path = UNSET_VALUE
|
|
||||||
@verbose_logging = UNSET_VALUE
|
|
||||||
|
|
||||||
@json = {}
|
|
||||||
@run_list = []
|
|
||||||
end
|
|
||||||
|
|
||||||
def encrypted_data_bag_secret=(value)
|
|
||||||
puts "DEPRECATION: Chef encrypted_data_bag_secret has no effect anymore."
|
|
||||||
puts "Remove this from your Vagrantfile since it'll be removed in the next"
|
|
||||||
puts "Vagrant version."
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def finalize!
|
def finalize!
|
||||||
@arguments = nil if @arguments == UNSET_VALUE
|
@binary_path = nil if @binary_path == UNSET_VALUE
|
||||||
@attempts = 1 if @attempts == UNSET_VALUE
|
@binary_env = nil if @binary_env == UNSET_VALUE
|
||||||
@binary_path = nil if @binary_path == UNSET_VALUE
|
@install = true if @install == UNSET_VALUE
|
||||||
@binary_env = nil if @binary_env == UNSET_VALUE
|
@log_level = :info if @log_level == UNSET_VALUE
|
||||||
@custom_config_path = nil if @custom_config_path == UNSET_VALUE
|
@prerelease = false if @prerelease == UNSET_VALUE
|
||||||
@environment = nil if @environment == UNSET_VALUE
|
@version = :latest if @version == UNSET_VALUE
|
||||||
@formatter = nil if @formatter == UNSET_VALUE
|
|
||||||
@http_proxy = nil if @http_proxy == UNSET_VALUE
|
|
||||||
@http_proxy_user = nil if @http_proxy_user == UNSET_VALUE
|
|
||||||
@http_proxy_pass = nil if @http_proxy_pass == UNSET_VALUE
|
|
||||||
@https_proxy = nil if @https_proxy == UNSET_VALUE
|
|
||||||
@https_proxy_user = nil if @https_proxy_user == UNSET_VALUE
|
|
||||||
@https_proxy_pass = nil if @https_proxy_pass == UNSET_VALUE
|
|
||||||
@log_level = :info if @log_level == UNSET_VALUE
|
|
||||||
@no_proxy = nil if @no_proxy == UNSET_VALUE
|
|
||||||
@node_name = nil if @node_name == UNSET_VALUE
|
|
||||||
@provisioning_path = nil if @provisioning_path == UNSET_VALUE
|
|
||||||
@file_backup_path = "/var/chef/backup" if @file_backup_path == UNSET_VALUE
|
|
||||||
@file_cache_path = "/var/chef/cache" if @file_cache_path == UNSET_VALUE
|
|
||||||
@verbose_logging = false if @verbose_logging == UNSET_VALUE
|
|
||||||
|
|
||||||
if @encrypted_data_bag_secret_key_path == UNSET_VALUE
|
# Make sure the install is a symbol if it's not a boolean
|
||||||
@encrypted_data_bag_secret_key_path = nil
|
if @install.respond_to?(:to_sym)
|
||||||
|
@install = @install.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make sure the version is a symbol if it's not a boolean
|
||||||
|
if @version.respond_to?(:to_sym)
|
||||||
|
@version = @version.to_sym
|
||||||
end
|
end
|
||||||
|
|
||||||
# Make sure the log level is a symbol
|
# Make sure the log level is a symbol
|
||||||
@log_level = @log_level.to_sym
|
@log_level = @log_level.to_sym
|
||||||
|
|
||||||
# Set the default provisioning path to be a unique path in /tmp
|
|
||||||
if !@provisioning_path
|
|
||||||
counter = self.class.get_and_update_counter(:chef_config)
|
|
||||||
@provisioning_path = "/tmp/vagrant-chef-#{counter}"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge(other)
|
# Like validate, but returns a list of errors to append.
|
||||||
super.tap do |result|
|
#
|
||||||
result.instance_variable_set(:@json, @json.merge(other.json))
|
# @return [Array<String>]
|
||||||
result.instance_variable_set(:@run_list, (@run_list + other.run_list))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Just like the normal configuration "validate" method except that
|
|
||||||
# it returns an array of errors that should be merged into some
|
|
||||||
# other error accumulator.
|
|
||||||
def validate_base(machine)
|
def validate_base(machine)
|
||||||
errors = _detected_errors
|
errors = _detected_errors
|
||||||
|
|
||||||
if @custom_config_path
|
if missing?(log_level)
|
||||||
expanded = File.expand_path(@custom_config_path, machine.env.root_path)
|
errors << I18n.t("vagrant.provisioners.chef.log_level_empty")
|
||||||
if !File.file?(expanded)
|
|
||||||
errors << I18n.t("vagrant.config.chef.custom_config_path_missing")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
errors
|
errors
|
||||||
end
|
end
|
||||||
|
|
||||||
# Adds a recipe to the run list
|
# Determine if the given string is "missing" (blank)
|
||||||
def add_recipe(name)
|
# @return [true, false]
|
||||||
name = "recipe[#{name}]" unless name =~ /^recipe\[(.+?)\]$/
|
def missing?(obj)
|
||||||
run_list << name
|
obj.to_s.strip.empty?
|
||||||
end
|
|
||||||
|
|
||||||
# Adds a role to the run list
|
|
||||||
def add_role(name)
|
|
||||||
name = "role[#{name}]" unless name =~ /^role\[(.+?)\]$/
|
|
||||||
run_list << name
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
require "vagrant/util/counter"
|
||||||
|
|
||||||
|
require_relative "base"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module Chef
|
||||||
|
module Config
|
||||||
|
# This is the config base for Chef provisioners that need a full Chef
|
||||||
|
# Runner object, like chef-solo or chef-client. For provisioners like
|
||||||
|
# chef-apply, these options are not valid
|
||||||
|
class BaseRunner < Base
|
||||||
|
attr_accessor :arguments
|
||||||
|
attr_accessor :attempts
|
||||||
|
attr_accessor :custom_config_path
|
||||||
|
attr_accessor :encrypted_data_bag_secret_key_path
|
||||||
|
attr_accessor :environment
|
||||||
|
attr_accessor :formatter
|
||||||
|
attr_accessor :http_proxy
|
||||||
|
attr_accessor :http_proxy_user
|
||||||
|
attr_accessor :http_proxy_pass
|
||||||
|
attr_accessor :https_proxy
|
||||||
|
attr_accessor :https_proxy_user
|
||||||
|
attr_accessor :https_proxy_pass
|
||||||
|
attr_accessor :json
|
||||||
|
attr_accessor :no_proxy
|
||||||
|
attr_accessor :node_name
|
||||||
|
attr_accessor :provisioning_path
|
||||||
|
attr_accessor :run_list
|
||||||
|
attr_accessor :file_cache_path
|
||||||
|
attr_accessor :file_backup_path
|
||||||
|
attr_accessor :verbose_logging
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
super
|
||||||
|
|
||||||
|
@arguments = UNSET_VALUE
|
||||||
|
@attempts = UNSET_VALUE
|
||||||
|
@custom_config_path = UNSET_VALUE
|
||||||
|
|
||||||
|
# /etc/chef/client.rb config options
|
||||||
|
@encrypted_data_bag_secret_key_path = UNSET_VALUE
|
||||||
|
@environment = UNSET_VALUE
|
||||||
|
@formatter = UNSET_VALUE
|
||||||
|
@http_proxy = UNSET_VALUE
|
||||||
|
@http_proxy_user = UNSET_VALUE
|
||||||
|
@http_proxy_pass = UNSET_VALUE
|
||||||
|
@https_proxy = UNSET_VALUE
|
||||||
|
@https_proxy_user = UNSET_VALUE
|
||||||
|
@https_proxy_pass = UNSET_VALUE
|
||||||
|
@no_proxy = UNSET_VALUE
|
||||||
|
@node_name = UNSET_VALUE
|
||||||
|
@provisioning_path = UNSET_VALUE
|
||||||
|
@file_cache_path = UNSET_VALUE
|
||||||
|
@file_backup_path = UNSET_VALUE
|
||||||
|
@verbose_logging = UNSET_VALUE
|
||||||
|
|
||||||
|
# Runner options
|
||||||
|
@json = {}
|
||||||
|
@run_list = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def encrypted_data_bag_secret=(value)
|
||||||
|
puts "DEPRECATION: Chef encrypted_data_bag_secret has no effect anymore."
|
||||||
|
puts "Remove this from your Vagrantfile since it'll be removed in the next"
|
||||||
|
puts "Vagrant version."
|
||||||
|
end
|
||||||
|
|
||||||
|
def finalize!
|
||||||
|
super
|
||||||
|
|
||||||
|
@arguments = nil if @arguments == UNSET_VALUE
|
||||||
|
@attempts = 1 if @attempts == UNSET_VALUE
|
||||||
|
@custom_config_path = nil if @custom_config_path == UNSET_VALUE
|
||||||
|
@environment = nil if @environment == UNSET_VALUE
|
||||||
|
@formatter = nil if @formatter == UNSET_VALUE
|
||||||
|
@http_proxy = nil if @http_proxy == UNSET_VALUE
|
||||||
|
@http_proxy_user = nil if @http_proxy_user == UNSET_VALUE
|
||||||
|
@http_proxy_pass = nil if @http_proxy_pass == UNSET_VALUE
|
||||||
|
@https_proxy = nil if @https_proxy == UNSET_VALUE
|
||||||
|
@https_proxy_user = nil if @https_proxy_user == UNSET_VALUE
|
||||||
|
@https_proxy_pass = nil if @https_proxy_pass == UNSET_VALUE
|
||||||
|
@no_proxy = nil if @no_proxy == UNSET_VALUE
|
||||||
|
@node_name = nil if @node_name == UNSET_VALUE
|
||||||
|
@provisioning_path = nil if @provisioning_path == UNSET_VALUE
|
||||||
|
@file_backup_path = "/var/chef/backup" if @file_backup_path == UNSET_VALUE
|
||||||
|
@file_cache_path = "/var/chef/cache" if @file_cache_path == UNSET_VALUE
|
||||||
|
@verbose_logging = false if @verbose_logging == UNSET_VALUE
|
||||||
|
|
||||||
|
if @encrypted_data_bag_secret_key_path == UNSET_VALUE
|
||||||
|
@encrypted_data_bag_secret_key_path = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set the default provisioning path to be a unique path in /tmp
|
||||||
|
if !@provisioning_path
|
||||||
|
counter = self.class.get_and_update_counter(:chef_config)
|
||||||
|
@provisioning_path = "/tmp/vagrant-chef-#{counter}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def merge(other)
|
||||||
|
super.tap do |result|
|
||||||
|
result.instance_variable_set(:@json, @json.merge(other.json))
|
||||||
|
result.instance_variable_set(:@run_list, (@run_list + other.run_list))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Just like the normal configuration "validate" method except that
|
||||||
|
# it returns an array of errors that should be merged into some
|
||||||
|
# other error accumulator.
|
||||||
|
def validate_base(machine)
|
||||||
|
errors = super
|
||||||
|
|
||||||
|
if @custom_config_path
|
||||||
|
expanded = File.expand_path(@custom_config_path, machine.env.root_path)
|
||||||
|
if !File.file?(expanded)
|
||||||
|
errors << I18n.t("vagrant.config.chef.custom_config_path_missing")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
errors
|
||||||
|
end
|
||||||
|
|
||||||
|
# Adds a recipe to the run list
|
||||||
|
def add_recipe(name)
|
||||||
|
name = "recipe[#{name}]" unless name =~ /^recipe\[(.+?)\]$/
|
||||||
|
run_list << name
|
||||||
|
end
|
||||||
|
|
||||||
|
# Adds a role to the run list
|
||||||
|
def add_role(name)
|
||||||
|
name = "role[#{name}]" unless name =~ /^role\[(.+?)\]$/
|
||||||
|
run_list << name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,9 +1,9 @@
|
||||||
|
require_relative "base"
|
||||||
|
|
||||||
module VagrantPlugins
|
module VagrantPlugins
|
||||||
module Chef
|
module Chef
|
||||||
module Config
|
module Config
|
||||||
class ChefApply < Vagrant.plugin("2", :config)
|
class ChefApply < Base
|
||||||
extend Vagrant::Util::Counter
|
|
||||||
|
|
||||||
# The raw recipe text (as a string) to execute via chef-apply.
|
# The raw recipe text (as a string) to execute via chef-apply.
|
||||||
# @return [String]
|
# @return [String]
|
||||||
attr_accessor :recipe
|
attr_accessor :recipe
|
||||||
|
@ -13,25 +13,17 @@ module VagrantPlugins
|
||||||
# @return [String]
|
# @return [String]
|
||||||
attr_accessor :upload_path
|
attr_accessor :upload_path
|
||||||
|
|
||||||
# The Chef log level.
|
|
||||||
# @return [String]
|
|
||||||
attr_accessor :log_level
|
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@recipe = UNSET_VALUE
|
super
|
||||||
|
|
||||||
@log_level = UNSET_VALUE
|
@recipe = UNSET_VALUE
|
||||||
@upload_path = UNSET_VALUE
|
@upload_path = UNSET_VALUE
|
||||||
end
|
end
|
||||||
|
|
||||||
def finalize!
|
def finalize!
|
||||||
@recipe = nil if @recipe == UNSET_VALUE
|
super
|
||||||
|
|
||||||
if @log_level == UNSET_VALUE
|
@recipe = nil if @recipe == UNSET_VALUE
|
||||||
@log_level = :info
|
|
||||||
else
|
|
||||||
@log_level = @log_level.to_sym
|
|
||||||
end
|
|
||||||
|
|
||||||
if @upload_path == UNSET_VALUE
|
if @upload_path == UNSET_VALUE
|
||||||
counter = self.class.get_and_update_counter(:chef_apply)
|
counter = self.class.get_and_update_counter(:chef_apply)
|
||||||
|
@ -40,28 +32,18 @@ module VagrantPlugins
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate(machine)
|
def validate(machine)
|
||||||
errors = _detected_errors
|
errors = validate_base(machine)
|
||||||
|
|
||||||
if missing(recipe)
|
if missing?(recipe)
|
||||||
errors << I18n.t("vagrant.provisioners.chef.recipe_empty")
|
errors << I18n.t("vagrant.provisioners.chef.recipe_empty")
|
||||||
end
|
end
|
||||||
|
|
||||||
if missing(log_level)
|
if missing?(upload_path)
|
||||||
errors << I18n.t("vagrant.provisioners.chef.log_level_empty")
|
|
||||||
end
|
|
||||||
|
|
||||||
if missing(upload_path)
|
|
||||||
errors << I18n.t("vagrant.provisioners.chef.upload_path_empty")
|
errors << I18n.t("vagrant.provisioners.chef.upload_path_empty")
|
||||||
end
|
end
|
||||||
|
|
||||||
{ "chef apply provisioner" => errors }
|
{ "chef apply provisioner" => errors }
|
||||||
end
|
end
|
||||||
|
|
||||||
# Determine if the given string is "missing" (blank)
|
|
||||||
# @return [true, false]
|
|
||||||
def missing(obj)
|
|
||||||
obj.to_s.strip.empty?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,16 +1,33 @@
|
||||||
require "vagrant/util/which"
|
require "vagrant/util/which"
|
||||||
|
|
||||||
require_relative "base"
|
require_relative "base_runner"
|
||||||
|
|
||||||
module VagrantPlugins
|
module VagrantPlugins
|
||||||
module Chef
|
module Chef
|
||||||
module Config
|
module Config
|
||||||
class ChefClient < Base
|
class ChefClient < BaseRunner
|
||||||
|
# The URL endpoint to the Chef Server.
|
||||||
|
# @return [String]
|
||||||
attr_accessor :chef_server_url
|
attr_accessor :chef_server_url
|
||||||
|
|
||||||
|
# The path on disk to the Chef client key,
|
||||||
|
# @return [String]
|
||||||
attr_accessor :client_key_path
|
attr_accessor :client_key_path
|
||||||
|
|
||||||
|
# Delete the client key when the VM is destroyed. Default is false.
|
||||||
|
# @return [true, false]
|
||||||
attr_accessor :delete_client
|
attr_accessor :delete_client
|
||||||
|
|
||||||
|
# Delete the node when the VM is destroyed. Default is false.
|
||||||
|
# @return [true, false]
|
||||||
attr_accessor :delete_node
|
attr_accessor :delete_node
|
||||||
|
|
||||||
|
# The path to the validation key on disk.
|
||||||
|
# @return [String]
|
||||||
attr_accessor :validation_key_path
|
attr_accessor :validation_key_path
|
||||||
|
|
||||||
|
# The name of the validation client.
|
||||||
|
# @return [String]
|
||||||
attr_accessor :validation_client_name
|
attr_accessor :validation_client_name
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
|
@ -36,8 +53,7 @@ module VagrantPlugins
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate(machine)
|
def validate(machine)
|
||||||
errors = _detected_errors
|
errors = validate_base(machine)
|
||||||
errors.concat(validate_base(machine))
|
|
||||||
|
|
||||||
if chef_server_url.to_s.strip.empty?
|
if chef_server_url.to_s.strip.empty?
|
||||||
errors << I18n.t("vagrant.config.chef.server_url_empty")
|
errors << I18n.t("vagrant.config.chef.server_url_empty")
|
||||||
|
|
|
@ -1,25 +1,60 @@
|
||||||
require_relative "base"
|
require_relative "base_runner"
|
||||||
|
|
||||||
module VagrantPlugins
|
module VagrantPlugins
|
||||||
module Chef
|
module Chef
|
||||||
module Config
|
module Config
|
||||||
class ChefSolo < Base
|
class ChefSolo < BaseRunner
|
||||||
|
# The path on disk where Chef cookbooks are stored.
|
||||||
|
# Default is "cookbooks".
|
||||||
|
# @return [String]
|
||||||
attr_accessor :cookbooks_path
|
attr_accessor :cookbooks_path
|
||||||
|
|
||||||
|
# The path where data bags are stored on disk.
|
||||||
|
# @return [String]
|
||||||
attr_accessor :data_bags_path
|
attr_accessor :data_bags_path
|
||||||
|
|
||||||
|
# The path where environments are stored on disk.
|
||||||
|
# @return [String]
|
||||||
attr_accessor :environments_path
|
attr_accessor :environments_path
|
||||||
|
|
||||||
|
# A URL download a remote recipe from. Note: you should use chef-apply
|
||||||
|
# instead.
|
||||||
|
#
|
||||||
|
# @deprecated
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
attr_accessor :recipe_url
|
attr_accessor :recipe_url
|
||||||
|
|
||||||
|
# The path where roles are stored on disk.
|
||||||
|
# @return [String]
|
||||||
attr_accessor :roles_path
|
attr_accessor :roles_path
|
||||||
|
|
||||||
|
# The type of synced folders to use.
|
||||||
|
# @return [String]
|
||||||
attr_accessor :synced_folder_type
|
attr_accessor :synced_folder_type
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super
|
super
|
||||||
|
|
||||||
@cookbooks_path = UNSET_VALUE
|
@cookbooks_path = UNSET_VALUE
|
||||||
@data_bags_path = UNSET_VALUE
|
@data_bags_path = UNSET_VALUE
|
||||||
@environments_path = UNSET_VALUE
|
@environments_path = UNSET_VALUE
|
||||||
@recipe_url = UNSET_VALUE
|
@recipe_url = UNSET_VALUE
|
||||||
@roles_path = UNSET_VALUE
|
@roles_path = UNSET_VALUE
|
||||||
@synced_folder_type = UNSET_VALUE
|
@synced_folder_type = UNSET_VALUE
|
||||||
|
end
|
||||||
|
|
||||||
|
# @deprecated This is deprecated in Chef and will be removed in Chef 12.
|
||||||
|
def recipe_url=(value)
|
||||||
|
puts "DEPRECATION: The 'recipe_url' setting for the Chef Solo"
|
||||||
|
puts "provisioner is deprecated. This value will be removed in"
|
||||||
|
puts "Chef 12. It is recommended you use the Chef Apply provisioner"
|
||||||
|
puts "instead. The 'recipe_url' setting will be removed in the next"
|
||||||
|
puts "version of Vagrant."
|
||||||
|
|
||||||
|
if value
|
||||||
|
@recipe_url = value
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def nfs=(value)
|
def nfs=(value)
|
||||||
|
@ -63,8 +98,7 @@ module VagrantPlugins
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate(machine)
|
def validate(machine)
|
||||||
errors = _detected_errors
|
errors = validate_base(machine)
|
||||||
errors.concat(validate_base(machine))
|
|
||||||
|
|
||||||
if [cookbooks_path].flatten.compact.empty?
|
if [cookbooks_path].flatten.compact.empty?
|
||||||
errors << I18n.t("vagrant.config.chef.cookbooks_path_empty")
|
errors << I18n.t("vagrant.config.chef.cookbooks_path_empty")
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
module VagrantPlugins
|
||||||
|
module Chef
|
||||||
|
class Installer
|
||||||
|
def initialize(machine, options = {})
|
||||||
|
@machine = machine
|
||||||
|
@version = options.fetch(:version, :latest)
|
||||||
|
@prerelease = options.fetch(:prerelease, :latest)
|
||||||
|
@force = options.fetch(:force, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
# This handles verifying the Chef installation, installing it if it was
|
||||||
|
# requested, and so on. This method will raise exceptions if things are
|
||||||
|
# wrong.
|
||||||
|
def ensure_installed
|
||||||
|
# If the guest cannot check if Chef is installed, just exit printing a
|
||||||
|
# warning...
|
||||||
|
if !@machine.guest.capability?(:chef_installed)
|
||||||
|
@machine.ui.warn(I18n.t("vagrant.chef_cant_detect"))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if !should_install_chef?
|
||||||
|
@machine.ui.info(I18n.t("vagrant.chef_already_installed",
|
||||||
|
version: @version.to_s))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
@machine.ui.detail(I18n.t("vagrant.chef_installing",
|
||||||
|
version: @version.to_s))
|
||||||
|
@machine.guest.capability(:chef_install, @version, @prerelease)
|
||||||
|
|
||||||
|
if !@machine.guest.capability(:chef_installed, @version)
|
||||||
|
raise Provisioner::Base::ChefError, :install_failed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Determine if Chef should be installed. Chef is installed if the "force"
|
||||||
|
# option is given or if the guest does not have Chef installed at the
|
||||||
|
# proper version.
|
||||||
|
def should_install_chef?
|
||||||
|
@force || !@machine.guest.capability(:chef_installed, @version)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,28 @@
|
||||||
|
module VagrantPlugins
|
||||||
|
module Chef
|
||||||
|
module Omnibus
|
||||||
|
OMNITRUCK = "https://www.getchef.com/chef/install.sh".freeze
|
||||||
|
|
||||||
|
# Read more about the Omnibus installer here:
|
||||||
|
# https://docs.getchef.com/install_omnibus.html
|
||||||
|
def build_command(version, prerelease = false)
|
||||||
|
command = "curl -sL #{OMNITRUCK} | sudo bash"
|
||||||
|
|
||||||
|
if prerelease || version != :latest
|
||||||
|
command << " -s --"
|
||||||
|
end
|
||||||
|
|
||||||
|
if prerelease
|
||||||
|
command << " -p"
|
||||||
|
end
|
||||||
|
|
||||||
|
if version != :latest
|
||||||
|
command << " -v \"#{version}\""
|
||||||
|
end
|
||||||
|
|
||||||
|
command
|
||||||
|
end
|
||||||
|
module_function :build_command
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -52,6 +52,21 @@ module VagrantPlugins
|
||||||
require_relative "provisioner/chef_zero"
|
require_relative "provisioner/chef_zero"
|
||||||
Provisioner::ChefZero
|
Provisioner::ChefZero
|
||||||
end
|
end
|
||||||
|
|
||||||
|
guest_capability(:linux, :chef_installed) do
|
||||||
|
require_relative "cap/linux/chef_installed"
|
||||||
|
Cap::Linux::ChefInstalled
|
||||||
|
end
|
||||||
|
|
||||||
|
guest_capability(:debian, :chef_install) do
|
||||||
|
require_relative "cap/debian/chef_install"
|
||||||
|
Cap::Debian::ChefInstall
|
||||||
|
end
|
||||||
|
|
||||||
|
guest_capability(:redhat, :chef_install) do
|
||||||
|
require_relative "cap/redhat/chef_install"
|
||||||
|
Cap::Redhat::ChefInstall
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,8 @@ require 'tempfile'
|
||||||
|
|
||||||
require "vagrant/util/template_renderer"
|
require "vagrant/util/template_renderer"
|
||||||
|
|
||||||
|
require_relative "../installer"
|
||||||
|
|
||||||
module VagrantPlugins
|
module VagrantPlugins
|
||||||
module Chef
|
module Chef
|
||||||
module Provisioner
|
module Provisioner
|
||||||
|
@ -13,6 +15,24 @@ module VagrantPlugins
|
||||||
error_namespace("vagrant.provisioners.chef")
|
error_namespace("vagrant.provisioners.chef")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def initialize(machine, config)
|
||||||
|
super
|
||||||
|
|
||||||
|
@logger = Log4r::Logger.new("vagrant::provisioners::chef")
|
||||||
|
end
|
||||||
|
|
||||||
|
def install_chef
|
||||||
|
return if !config.install
|
||||||
|
|
||||||
|
@logger.info("Checking for Chef installation...")
|
||||||
|
installer = Installer.new(@machine,
|
||||||
|
force: config.install == :force,
|
||||||
|
version: config.version,
|
||||||
|
prerelease: config.prerelease,
|
||||||
|
)
|
||||||
|
installer.ensure_installed
|
||||||
|
end
|
||||||
|
|
||||||
def verify_binary(binary)
|
def verify_binary(binary)
|
||||||
# Checks for the existence of chef binary and error if it
|
# Checks for the existence of chef binary and error if it
|
||||||
# doesn't exist.
|
# doesn't exist.
|
||||||
|
@ -20,7 +40,8 @@ module VagrantPlugins
|
||||||
"which #{binary}",
|
"which #{binary}",
|
||||||
error_class: ChefError,
|
error_class: ChefError,
|
||||||
error_key: :chef_not_detected,
|
error_key: :chef_not_detected,
|
||||||
binary: binary)
|
binary: binary,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# This returns the command to run Chef for the given client
|
# This returns the command to run Chef for the given client
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
require "tempfile"
|
require "tempfile"
|
||||||
|
|
||||||
|
require_relative "base"
|
||||||
|
|
||||||
module VagrantPlugins
|
module VagrantPlugins
|
||||||
module Chef
|
module Chef
|
||||||
module Provisioner
|
module Provisioner
|
||||||
class ChefApply < Vagrant.plugin("2", :provisioner)
|
class ChefApply < Base
|
||||||
def provision
|
def provision
|
||||||
|
install_chef
|
||||||
|
verify_binary(chef_binary_path("chef-apply"))
|
||||||
|
|
||||||
command = "chef-apply"
|
command = "chef-apply"
|
||||||
command << " --log-level #{config.log_level}"
|
command << " \"#{target_recipe_path}\""
|
||||||
command << " #{config.upload_path}"
|
command << " --log_level #{config.log_level}"
|
||||||
|
|
||||||
user = @machine.ssh_info[:username]
|
user = @machine.ssh_info[:username]
|
||||||
|
|
||||||
|
@ -18,7 +23,7 @@ module VagrantPlugins
|
||||||
# Upload the recipe
|
# Upload the recipe
|
||||||
upload_recipe
|
upload_recipe
|
||||||
|
|
||||||
@machine.ui.info(I18n.t("vagrant.provisioners.chef.running_chef_apply",
|
@machine.ui.info(I18n.t("vagrant.provisioners.chef.running_apply",
|
||||||
script: config.path)
|
script: config.path)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,6 +39,12 @@ module VagrantPlugins
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# The destination (on the guest) where the recipe will live
|
||||||
|
# @return [String]
|
||||||
|
def target_recipe_path
|
||||||
|
File.join(config.upload_path, "recipe.rb")
|
||||||
|
end
|
||||||
|
|
||||||
# Write the raw recipe contents to a tempfile and upload that to the
|
# Write the raw recipe contents to a tempfile and upload that to the
|
||||||
# machine.
|
# machine.
|
||||||
def upload_recipe
|
def upload_recipe
|
||||||
|
@ -43,8 +54,7 @@ module VagrantPlugins
|
||||||
file.rewind
|
file.rewind
|
||||||
|
|
||||||
# Upload the tempfile to the guest
|
# Upload the tempfile to the guest
|
||||||
destination = File.join(config.upload_path, "recipe.rb")
|
@machine.communicate.upload(file.path, target_recipe_path)
|
||||||
@machine.communicate.upload(file.path, destination)
|
|
||||||
ensure
|
ensure
|
||||||
# Delete our template
|
# Delete our template
|
||||||
file.close
|
file.close
|
||||||
|
|
|
@ -18,6 +18,7 @@ module VagrantPlugins
|
||||||
end
|
end
|
||||||
|
|
||||||
def provision
|
def provision
|
||||||
|
install_chef
|
||||||
verify_binary(chef_binary_path("chef-client"))
|
verify_binary(chef_binary_path("chef-client"))
|
||||||
chown_provisioning_folder
|
chown_provisioning_folder
|
||||||
create_client_key_folder
|
create_client_key_folder
|
||||||
|
|
|
@ -35,6 +35,7 @@ module VagrantPlugins
|
||||||
end
|
end
|
||||||
|
|
||||||
def provision
|
def provision
|
||||||
|
install_chef
|
||||||
# Verify that the proper shared folders exist.
|
# Verify that the proper shared folders exist.
|
||||||
check = []
|
check = []
|
||||||
@shared_folders.each do |type, local_path, remote_path|
|
@shared_folders.each do |type, local_path, remote_path|
|
||||||
|
|
|
@ -89,14 +89,42 @@ Vagrant.configure(2) do |config|
|
||||||
# puppet.manifest_file = "default.pp"
|
# puppet.manifest_file = "default.pp"
|
||||||
# end
|
# end
|
||||||
|
|
||||||
# Enable provisioning with chef solo, specifying a cookbooks path, roles
|
# Enable provisioning with Chef Solo, specifying a cookbooks path, roles
|
||||||
# path, and data_bags path (all relative to this Vagrantfile), and adding
|
# path, and data_bags path (all relative to this Vagrantfile), and adding
|
||||||
# some recipes and/or roles.
|
# some recipes and/or roles.
|
||||||
#
|
#
|
||||||
# config.vm.provision "chef_solo" do |chef|
|
# config.vm.provision "chef_solo" do |chef|
|
||||||
# chef.cookbooks_path = "../my-recipes/cookbooks"
|
# chef.cookbooks_path = "~/chef/cookbooks"
|
||||||
# chef.roles_path = "../my-recipes/roles"
|
# chef.roles_path = "~/chef/roles"
|
||||||
# chef.data_bags_path = "../my-recipes/data_bags"
|
# chef.data_bags_path = "~/chef/data_bags"
|
||||||
|
#
|
||||||
|
# chef.add_recipe "mysql"
|
||||||
|
# chef.add_role "web"
|
||||||
|
#
|
||||||
|
# chef.json = { mysql_password: "foo" }
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Chef Solo will automatically install the latest version of Chef for you.
|
||||||
|
# This can be configured in the provisioner block:
|
||||||
|
#
|
||||||
|
# config.vm.provision "chef_solo" do |chef|
|
||||||
|
# chef.version = "11.16.4"
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Alternative you can disable the installation of Chef entirely:
|
||||||
|
#
|
||||||
|
# config.vm.provision "chef_solo" do |chef|
|
||||||
|
# chef.install = false
|
||||||
|
# end
|
||||||
|
|
||||||
|
# Enable provisioning with Chef Zero. The Chef Zero provisioner accepts the
|
||||||
|
# exact same parameter as the Chef Solo provisioner:
|
||||||
|
#
|
||||||
|
# config.vm.provision "chef_zero" do |chef|
|
||||||
|
# chef.cookbooks_path = "~/chef/cookbooks"
|
||||||
|
# chef.roles_path = "~/chef/roles"
|
||||||
|
# chef.data_bags_path = "~/chef/data_bags"
|
||||||
|
#
|
||||||
# chef.add_recipe "mysql"
|
# chef.add_recipe "mysql"
|
||||||
# chef.add_role "web"
|
# chef.add_role "web"
|
||||||
#
|
#
|
||||||
|
@ -104,10 +132,10 @@ Vagrant.configure(2) do |config|
|
||||||
# chef.json = { mysql_password: "foo" }
|
# chef.json = { mysql_password: "foo" }
|
||||||
# end
|
# end
|
||||||
|
|
||||||
# Enable provisioning with chef server, specifying the chef server URL,
|
# Enable provisioning with Chef Server, specifying the chef server URL,
|
||||||
# and the path to the validation key (relative to this Vagrantfile).
|
# and the path to the validation key (relative to this Vagrantfile).
|
||||||
#
|
#
|
||||||
# The Opscode Platform uses HTTPS. Substitute your organization for
|
# The Hosted Chef platform uses HTTPS. Substitute your organization for
|
||||||
# ORGNAME in the URL and validation key.
|
# ORGNAME in the URL and validation key.
|
||||||
#
|
#
|
||||||
# If you have your own Chef Server, use the appropriate URL, which may be
|
# If you have your own Chef Server, use the appropriate URL, which may be
|
||||||
|
@ -115,15 +143,44 @@ Vagrant.configure(2) do |config|
|
||||||
# validation key to validation.pem.
|
# validation key to validation.pem.
|
||||||
#
|
#
|
||||||
# config.vm.provision "chef_client" do |chef|
|
# config.vm.provision "chef_client" do |chef|
|
||||||
# chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
|
# chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
|
||||||
# chef.validation_key_path = "ORGNAME-validator.pem"
|
# chef.validation_key_path = "ORGNAME-validator.pem"
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# If you're using the Opscode platform, your validator client is
|
# If you're using the Hosted Chef platform, your validator client is
|
||||||
# ORGNAME-validator, replacing ORGNAME with your organization name.
|
# ORGNAME-validator, replacing ORGNAME with your organization name.
|
||||||
#
|
#
|
||||||
# If you have your own Chef Server, the default validation client name is
|
# If you have your own Chef Server, the default validation client name is
|
||||||
# chef-validator, unless you changed the configuration.
|
# chef-validator, unless you changed the configuration.
|
||||||
#
|
#
|
||||||
# chef.validation_client_name = "ORGNAME-validator"
|
# chef.validation_client_name = "ORGNAME-validator"
|
||||||
|
#
|
||||||
|
# Chef Client will automatically install the latest version of Chef for you.
|
||||||
|
# This can be configured in the provisioner block:
|
||||||
|
#
|
||||||
|
# config.vm.provision "chef_client" do |chef|
|
||||||
|
# chef.version = "11.16.4"
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Alternative you can disable the installation of Chef entirely:
|
||||||
|
#
|
||||||
|
# config.vm.provision "chef_client" do |chef|
|
||||||
|
# chef.install = false
|
||||||
|
# end
|
||||||
|
|
||||||
|
# Enable provisioning with Chef Apply, specifying an inline recipe to execute
|
||||||
|
# on the target system.
|
||||||
|
#
|
||||||
|
# config.vm.provision "chef_apply" do |chef|
|
||||||
|
# chef.recipe = <<-RECIPE
|
||||||
|
# package "curl"
|
||||||
|
# RECIPE
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Chef Apply will automatically install the latest version of Chef for you.
|
||||||
|
# This can be configured in the provisioner block:
|
||||||
|
#
|
||||||
|
# config.vm.provision "chef_apply" do |chef|
|
||||||
|
# chef.version = "11.16.4"
|
||||||
|
# end
|
||||||
end
|
end
|
||||||
|
|
|
@ -87,6 +87,14 @@ en:
|
||||||
CFEngine running in "single run" mode. Will execute one file.
|
CFEngine running in "single run" mode. Will execute one file.
|
||||||
cfengine_single_run_execute: |-
|
cfengine_single_run_execute: |-
|
||||||
Executing run file for CFEngine...
|
Executing run file for CFEngine...
|
||||||
|
chef_cant_detect: |-
|
||||||
|
Vagrant does not support detecting whether Chef is installed
|
||||||
|
for the guest OS running in the machine. Vagrant will assume it is
|
||||||
|
installed and attempt to continue.
|
||||||
|
chef_already_installed: |-
|
||||||
|
Detected Chef (%{version}) is already installed
|
||||||
|
chef_installing: |-
|
||||||
|
Installing Chef (%{version})...
|
||||||
chef_client_cleanup_failed: |-
|
chef_client_cleanup_failed: |-
|
||||||
Cleaning up the '%{deletable}' for Chef failed. The stdout and
|
Cleaning up the '%{deletable}' for Chef failed. The stdout and
|
||||||
stderr are shown below. Vagrant will continue destroying the machine,
|
stderr are shown below. Vagrant will continue destroying the machine,
|
||||||
|
@ -1754,6 +1762,16 @@ en:
|
||||||
"The cookbook path '%{path}' doesn't exist. Ignoring..."
|
"The cookbook path '%{path}' doesn't exist. Ignoring..."
|
||||||
json: "Generating chef JSON and uploading..."
|
json: "Generating chef JSON and uploading..."
|
||||||
client_key_folder: "Creating folder to hold client key..."
|
client_key_folder: "Creating folder to hold client key..."
|
||||||
|
install_failed: |-
|
||||||
|
Vagrant could not detect Chef on the guest! Even after Vagrant
|
||||||
|
attempted to install Chef, it could still not find Chef on the system.
|
||||||
|
Please make sure you are connected to the Internet and can access
|
||||||
|
Chef's package distribution servers. If you already have Chef
|
||||||
|
installed on this guest, you can disable the automatic Chef detection
|
||||||
|
by setting the 'install' option in the Chef configuration section of
|
||||||
|
your Vagrantfile:
|
||||||
|
|
||||||
|
chef.install = false
|
||||||
log_level_empty: |-
|
log_level_empty: |-
|
||||||
The Chef provisioner requires a log level. If you did not set a
|
The Chef provisioner requires a log level. If you did not set a
|
||||||
log level, this is probably a bug and should be reported.
|
log level, this is probably a bug and should be reported.
|
||||||
|
@ -1765,7 +1783,7 @@ en:
|
||||||
guest.
|
guest.
|
||||||
running_client: "Running chef-client..."
|
running_client: "Running chef-client..."
|
||||||
running_client_again: "Running chef-client again (failed to converge)..."
|
running_client_again: "Running chef-client again (failed to converge)..."
|
||||||
running_client_apply: "Running chef-apply..."
|
running_apply: "Running chef-apply..."
|
||||||
running_solo: "Running chef-solo..."
|
running_solo: "Running chef-solo..."
|
||||||
running_solo_again: "Running chef-solo again (failed to converge)..."
|
running_solo_again: "Running chef-solo again (failed to converge)..."
|
||||||
missing_shared_folders: |-
|
missing_shared_folders: |-
|
||||||
|
|
|
@ -0,0 +1,255 @@
|
||||||
|
require_relative "../../../../base"
|
||||||
|
|
||||||
|
require Vagrant.source_root.join("plugins/provisioners/chef/config/base_runner")
|
||||||
|
|
||||||
|
describe VagrantPlugins::Chef::Config::BaseRunner do
|
||||||
|
include_context "unit"
|
||||||
|
|
||||||
|
subject { described_class.new }
|
||||||
|
|
||||||
|
let(:machine) { double("machine") }
|
||||||
|
|
||||||
|
describe "#arguments" do
|
||||||
|
it "defaults to nil" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.arguments).to be(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#attempts" do
|
||||||
|
it "defaults to 1" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.attempts).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#custom_config_path" do
|
||||||
|
it "defaults to nil" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.custom_config_path).to be(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#environment" do
|
||||||
|
it "defaults to nil" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.environment).to be(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#encrypted_data_bag_secret_key_path" do
|
||||||
|
it "defaults to nil" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.encrypted_data_bag_secret_key_path).to be(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#formatter" do
|
||||||
|
it "defaults to nil" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.formatter).to be(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#http_proxy" do
|
||||||
|
it "defaults to nil" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.http_proxy).to be(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#http_proxy_user" do
|
||||||
|
it "defaults to nil" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.http_proxy_user).to be(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#http_proxy_pass" do
|
||||||
|
it "defaults to nil" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.http_proxy_pass).to be(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#https_proxy" do
|
||||||
|
it "defaults to nil" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.https_proxy).to be(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#https_proxy_user" do
|
||||||
|
it "defaults to nil" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.https_proxy_user).to be(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#https_proxy_pass" do
|
||||||
|
it "defaults to nil" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.https_proxy_pass).to be(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#log_level" do
|
||||||
|
it "defaults to :info" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.log_level).to be(:info)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is converted to a symbol" do
|
||||||
|
subject.log_level = "foo"
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.log_level).to eq(:foo)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#no_proxy" do
|
||||||
|
it "defaults to nil" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.no_proxy).to be(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#node_name" do
|
||||||
|
it "defaults to nil" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.node_name).to be(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#provisioning_path" do
|
||||||
|
it "defaults to a tmp_path" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.provisioning_path).to match(%r{/tmp/vagrant-chef-\d+})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#file_backup_path" do
|
||||||
|
it "defaults to /var/chef/backup" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.file_backup_path).to eq("/var/chef/backup")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#file_cache_path" do
|
||||||
|
it "defaults to /var/chef/cache" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.file_cache_path).to eq("/var/chef/cache")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#verbose_logging" do
|
||||||
|
it "defaults to false" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.verbose_logging).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#run_list" do
|
||||||
|
it "defaults to an empty array" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.run_list).to be_a(Array)
|
||||||
|
expect(subject.run_list).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#json" do
|
||||||
|
it "defaults to an empty hash" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.json).to be_a(Hash)
|
||||||
|
expect(subject.json).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#add_recipe" do
|
||||||
|
context "when the prefix is given" do
|
||||||
|
it "adds the value to the run_list" do
|
||||||
|
subject.add_recipe("recipe[foo::bar]")
|
||||||
|
expect(subject.run_list).to eq %w(recipe[foo::bar])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the prefix is not given" do
|
||||||
|
it "adds the prefixed value to the run_list" do
|
||||||
|
subject.add_recipe("foo::bar")
|
||||||
|
expect(subject.run_list).to eq %w(recipe[foo::bar])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#add_role" do
|
||||||
|
context "when the prefix is given" do
|
||||||
|
it "adds the value to the run_list" do
|
||||||
|
subject.add_role("role[foo]")
|
||||||
|
expect(subject.run_list).to eq %w(role[foo])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the prefix is not given" do
|
||||||
|
it "adds the prefixed value to the run_list" do
|
||||||
|
subject.add_role("foo")
|
||||||
|
expect(subject.run_list).to eq %w(role[foo])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#validate_base" do
|
||||||
|
context "when #custom_config_path does not exist" do
|
||||||
|
let(:path) { "/path/to/file" }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(File).to receive(:file?)
|
||||||
|
.with(path)
|
||||||
|
.and_return(false)
|
||||||
|
|
||||||
|
allow(machine).to receive(:env)
|
||||||
|
.and_return(double("env",
|
||||||
|
root_path: "",
|
||||||
|
))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an error" do
|
||||||
|
subject.custom_config_path = path
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
expect(subject.validate_base(machine))
|
||||||
|
.to eq ['Path specified for "custom_config_path" does not exist.']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#merge" do
|
||||||
|
it "merges the json hash" do
|
||||||
|
a = described_class.new.tap do |i|
|
||||||
|
i.json = { "foo" => "bar" }
|
||||||
|
end
|
||||||
|
b = described_class.new.tap do |i|
|
||||||
|
i.json = { "zip" => "zap" }
|
||||||
|
end
|
||||||
|
|
||||||
|
result = a.merge(b)
|
||||||
|
expect(result.json).to eq(
|
||||||
|
"foo" => "bar",
|
||||||
|
"zip" => "zap",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "appends the run_list array" do
|
||||||
|
a = described_class.new.tap do |i|
|
||||||
|
i.run_list = ["recipe[foo::bar]"]
|
||||||
|
end
|
||||||
|
b = described_class.new.tap do |i|
|
||||||
|
i.run_list = ["recipe[zip::zap]"]
|
||||||
|
end
|
||||||
|
|
||||||
|
result = a.merge(b)
|
||||||
|
expect(result.run_list).to eq %w(
|
||||||
|
recipe[foo::bar]
|
||||||
|
recipe[zip::zap]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,20 +9,6 @@ describe VagrantPlugins::Chef::Config::Base do
|
||||||
|
|
||||||
let(:machine) { double("machine") }
|
let(:machine) { double("machine") }
|
||||||
|
|
||||||
describe "#arguments" do
|
|
||||||
it "defaults to nil" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.arguments).to be(nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#attempts" do
|
|
||||||
it "defaults to 1" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.attempts).to eq(1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#binary_path" do
|
describe "#binary_path" do
|
||||||
it "defaults to nil" do
|
it "defaults to nil" do
|
||||||
subject.finalize!
|
subject.finalize!
|
||||||
|
@ -37,66 +23,16 @@ describe VagrantPlugins::Chef::Config::Base do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#custom_config_path" do
|
describe "#install" do
|
||||||
it "defaults to nil" do
|
it "defaults to true" do
|
||||||
subject.finalize!
|
subject.finalize!
|
||||||
expect(subject.custom_config_path).to be(nil)
|
expect(subject.install).to be(true)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
describe "#environment" do
|
it "is converted to a symbol" do
|
||||||
it "defaults to nil" do
|
subject.install = "force"
|
||||||
subject.finalize!
|
subject.finalize!
|
||||||
expect(subject.environment).to be(nil)
|
expect(subject.install).to eq(:force)
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#formatter" do
|
|
||||||
it "defaults to nil" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.formatter).to be(nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#http_proxy" do
|
|
||||||
it "defaults to nil" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.http_proxy).to be(nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#http_proxy_user" do
|
|
||||||
it "defaults to nil" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.http_proxy_user).to be(nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#http_proxy_pass" do
|
|
||||||
it "defaults to nil" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.http_proxy_pass).to be(nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#https_proxy" do
|
|
||||||
it "defaults to nil" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.https_proxy).to be(nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#https_proxy_user" do
|
|
||||||
it "defaults to nil" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.https_proxy_user).to be(nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#https_proxy_pass" do
|
|
||||||
it "defaults to nil" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.https_proxy_pass).to be(nil)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -113,150 +49,23 @@ describe VagrantPlugins::Chef::Config::Base do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#no_proxy" do
|
describe "#prerelease" do
|
||||||
it "defaults to nil" do
|
it "defaults to true" do
|
||||||
subject.finalize!
|
subject.finalize!
|
||||||
expect(subject.no_proxy).to be(nil)
|
expect(subject.prerelease).to be(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#node_name" do
|
describe "#version" do
|
||||||
it "defaults to nil" do
|
it "defaults to :latest" do
|
||||||
subject.finalize!
|
subject.finalize!
|
||||||
expect(subject.node_name).to be(nil)
|
expect(subject.version).to eq(:latest)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
describe "#provisioning_path" do
|
it "converts the string 'latest' to a symbol" do
|
||||||
it "defaults to a tmp_path" do
|
subject.version = "latest"
|
||||||
subject.finalize!
|
subject.finalize!
|
||||||
expect(subject.provisioning_path).to match(%r{/tmp/vagrant-chef-\d+})
|
expect(subject.version).to eq(:latest)
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#file_backup_path" do
|
|
||||||
it "defaults to /var/chef/backup" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.file_backup_path).to eq("/var/chef/backup")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#file_cache_path" do
|
|
||||||
it "defaults to /var/chef/cache" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.file_cache_path).to eq("/var/chef/cache")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#verbose_logging" do
|
|
||||||
it "defaults to false" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.verbose_logging).to be(false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#run_list" do
|
|
||||||
it "defaults to an empty array" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.run_list).to be_a(Array)
|
|
||||||
expect(subject.run_list).to be_empty
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#json" do
|
|
||||||
it "defaults to an empty hash" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.json).to be_a(Hash)
|
|
||||||
expect(subject.json).to be_empty
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#add_recipe" do
|
|
||||||
context "when the prefix is given" do
|
|
||||||
it "adds the value to the run_list" do
|
|
||||||
subject.add_recipe("recipe[foo::bar]")
|
|
||||||
expect(subject.run_list).to eq %w(recipe[foo::bar])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the prefix is not given" do
|
|
||||||
it "adds the prefixed value to the run_list" do
|
|
||||||
subject.add_recipe("foo::bar")
|
|
||||||
expect(subject.run_list).to eq %w(recipe[foo::bar])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#add_role" do
|
|
||||||
context "when the prefix is given" do
|
|
||||||
it "adds the value to the run_list" do
|
|
||||||
subject.add_role("role[foo]")
|
|
||||||
expect(subject.run_list).to eq %w(role[foo])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the prefix is not given" do
|
|
||||||
it "adds the prefixed value to the run_list" do
|
|
||||||
subject.add_role("foo")
|
|
||||||
expect(subject.run_list).to eq %w(role[foo])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#validate_base" do
|
|
||||||
context "when #custom_config_path does not exist" do
|
|
||||||
let(:path) { "/path/to/file" }
|
|
||||||
|
|
||||||
before do
|
|
||||||
allow(File).to receive(:file?)
|
|
||||||
.with(path)
|
|
||||||
.and_return(false)
|
|
||||||
|
|
||||||
allow(machine).to receive(:env)
|
|
||||||
.and_return(double("env",
|
|
||||||
root_path: "",
|
|
||||||
))
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns an error" do
|
|
||||||
subject.custom_config_path = path
|
|
||||||
subject.finalize!
|
|
||||||
|
|
||||||
expect(subject.validate_base(machine))
|
|
||||||
.to eq ['Path specified for "custom_config_path" does not exist.']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#merge" do
|
|
||||||
it "merges the json hash" do
|
|
||||||
a = described_class.new.tap do |i|
|
|
||||||
i.json = { "foo" => "bar" }
|
|
||||||
end
|
|
||||||
b = described_class.new.tap do |i|
|
|
||||||
i.json = { "zip" => "zap" }
|
|
||||||
end
|
|
||||||
|
|
||||||
result = a.merge(b)
|
|
||||||
expect(result.json).to eq(
|
|
||||||
"foo" => "bar",
|
|
||||||
"zip" => "zap",
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "appends the run_list array" do
|
|
||||||
a = described_class.new.tap do |i|
|
|
||||||
i.run_list = ["recipe[foo::bar]"]
|
|
||||||
end
|
|
||||||
b = described_class.new.tap do |i|
|
|
||||||
i.run_list = ["recipe[zip::zap]"]
|
|
||||||
end
|
|
||||||
|
|
||||||
result = a.merge(b)
|
|
||||||
expect(result.run_list).to eq %w(
|
|
||||||
recipe[foo::bar]
|
|
||||||
recipe[zip::zap]
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,19 +20,6 @@ describe VagrantPlugins::Chef::Config::ChefApply do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#log_level" do
|
|
||||||
it "defaults to :info" do
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.log_level).to be(:info)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is converted to a symbol" do
|
|
||||||
subject.log_level = "foo"
|
|
||||||
subject.finalize!
|
|
||||||
expect(subject.log_level).to eq(:foo)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#upload_path" do
|
describe "#upload_path" do
|
||||||
it "defaults to /tmp/vagrant-chef-apply.rb" do
|
it "defaults to /tmp/vagrant-chef-apply.rb" do
|
||||||
subject.finalize!
|
subject.finalize!
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
require_relative "../../../base"
|
||||||
|
|
||||||
|
require Vagrant.source_root.join("plugins/provisioners/chef/omnibus")
|
||||||
|
|
||||||
|
describe VagrantPlugins::Chef::Omnibus, :focus do
|
||||||
|
let(:prefix) { "curl -sL #{described_class.const_get(:OMNITRUCK)}" }
|
||||||
|
|
||||||
|
let(:version) { :latest }
|
||||||
|
let(:prerelease) { false }
|
||||||
|
|
||||||
|
let(:build_command) { described_class.build_command(version, prerelease) }
|
||||||
|
|
||||||
|
context "when prerelease is given" do
|
||||||
|
let(:prerelease) { true }
|
||||||
|
|
||||||
|
it "returns the correct command" do
|
||||||
|
expect(build_command).to eq("#{prefix} | sudo bash -s -- -p")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when version is :latest" do
|
||||||
|
let(:version) { :latest }
|
||||||
|
|
||||||
|
it "returns the correct command" do
|
||||||
|
expect(build_command).to eq("#{prefix} | sudo bash")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when version is a string" do
|
||||||
|
let(:version) { "1.2.3" }
|
||||||
|
|
||||||
|
it "returns the correct command" do
|
||||||
|
expect(build_command).to eq("#{prefix} | sudo bash -s -- -v \"1.2.3\"")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when prerelease and version are given" do
|
||||||
|
let(:version) { "1.2.3" }
|
||||||
|
let(:prerelease) { true }
|
||||||
|
|
||||||
|
it "returns the correct command" do
|
||||||
|
expect(build_command).to eq("#{prefix} | sudo bash -s -- -p -v \"1.2.3\"")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue