Merge pull request #11579 from soapy1/provisioner-communicator_required

Provisioner communicator required
This commit is contained in:
Sophia Castellarin 2020-07-17 10:45:34 -05:00 committed by GitHub
commit 3d4b721a22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 153 additions and 20 deletions

View File

@ -25,6 +25,7 @@ module Vagrant
autoload :HandleBox, "vagrant/action/builtin/handle_box"
autoload :HandleBoxUrl, "vagrant/action/builtin/handle_box_url"
autoload :HandleForwardedPortCollisions, "vagrant/action/builtin/handle_forwarded_port_collisions"
autoload :HasProvisioner, "vagrant/action/builtin/has_provisioner"
autoload :IsEnvSet, "vagrant/action/builtin/is_env_set"
autoload :IsState, "vagrant/action/builtin/is_state"
autoload :Lock, "vagrant/action/builtin/lock"

View File

@ -0,0 +1,36 @@
module Vagrant
module Action
module Builtin
# This middleware is used with Call to test if this machine
# has available provisioners
class HasProvisioner
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::action::builtin::has_provisioner")
end
def call(env)
machine = env[:machine]
if machine.provider.capability?(:has_communicator)
has_communicator = machine.provider.capability(:has_communicator)
else
has_communicator = true
end
env[:skip] = []
if !has_communicator
machine.config.vm.provisioners.each do |p|
if p.communicator_required
env[:skip].push(p)
@logger.info("Skipping running provisioner #{p.name || 'no name'}, type: #{p.type}")
p.run = :never
end
end
end
@app.call(env)
end
end
end
end
end

View File

@ -49,6 +49,7 @@ module Vagrant
run: provisioner.run,
before: provisioner.before,
after: provisioner.after,
communicator_required: provisioner.communicator_required,
}
# Return the result

View File

@ -410,6 +410,8 @@ module VagrantPlugins
prov.preserve_order = !!options.delete(:preserve_order) if \
options.key?(:preserve_order)
prov.run = options.delete(:run) if options.key?(:run)
prov.communicator_required = options.delete(:communicator_required) if options.key?(:communicator_required)
prov.add_config(options, &block)
nil
end

View File

@ -56,6 +56,12 @@ module VagrantPlugins
# @return [String, Symbol]
attr_accessor :after
# Boolean, when true signifies that some communicator must
# be available in order for the provisioner to run.
#
# @return [Boolean]
attr_accessor :communicator_required
def initialize(name, type, **options)
@logger = Log4r::Logger.new("vagrant::config::vm::provisioner")
@logger.debug("Provisioner defined: #{name}")
@ -69,6 +75,7 @@ module VagrantPlugins
@type = type
@before = options[:before]
@after = options[:after]
@communicator_required = options.fetch(:communicator_required, true)
# Attempt to find the provisioner...
if !Vagrant.plugin("2").manager.provisioners[type]
@ -118,6 +125,10 @@ module VagrantPlugins
provisioner_names = provisioners.map { |i| i.name.to_s if i.name != name }.compact
if ![TrueClass, FalseClass].include?(@communicator_required.class)
errors << I18n.t("vagrant.provisioners.base.wrong_type", opt: "communicator_required", type: "boolean")
end
if @before && @after
errors << I18n.t("vagrant.provisioners.base.both_before_after_set")
end
@ -127,7 +138,7 @@ module VagrantPlugins
if @before.is_a?(Symbol) && !VALID_BEFORE_AFTER_TYPES.include?(@before)
errors << I18n.t("vagrant.provisioners.base.invalid_alias_value", opt: "before", alias: VALID_BEFORE_AFTER_TYPES.join(", "))
elsif !@before.is_a?(String) && !VALID_BEFORE_AFTER_TYPES.include?(@before)
errors << I18n.t("vagrant.provisioners.base.wrong_type", opt: "before")
errors << I18n.t("vagrant.provisioners.base.wrong_type", opt: "before", type: "string")
end
if !provisioner_names.include?(@before)
@ -153,7 +164,7 @@ module VagrantPlugins
if @after.is_a?(Symbol)
errors << I18n.t("vagrant.provisioners.base.invalid_alias_value", opt: "after", alias: VALID_BEFORE_AFTER_TYPES.join(", "))
elsif !@after.is_a?(String)
errors << I18n.t("vagrant.provisioners.base.wrong_type", opt: "after")
errors << I18n.t("vagrant.provisioners.base.wrong_type", opt: "after", type: "string")
end
if !provisioner_names.include?(@after)

View File

@ -66,14 +66,8 @@ module VagrantPlugins
next
end
b3.use Call, HasSSH do |env3, b4|
if env3[:result]
b4.use Provision
else
b4.use Message,
I18n.t("docker_provider.messages.provision_no_ssh"),
post: true
end
b3.use Call, HasProvisioner do |env3, b4|
b4.use Provision
end
end
end
@ -216,14 +210,8 @@ module VagrantPlugins
Vagrant::Action::Builder.new.tap do |b|
b.use Call, IsState, :running do |env, b2|
if env[:machine_action] != :run_command
b2.use Call, HasSSH do |env2, b3|
if env2[:result]
b3.use Provision
else
b3.use Message,
I18n.t("docker_provider.messages.provision_no_ssh"),
post: true
end
b2.use Call, HasProvisioner do |env2, b3|
b3.use Provision
end
end

View File

@ -0,0 +1,11 @@
module VagrantPlugins
module DockerProvider
module Cap
module HasCommunicator
def self.has_communicator(machine)
return machine.provider_config.has_ssh
end
end
end
end
end

View File

@ -67,6 +67,11 @@ module VagrantPlugins
Cap::ProxyMachine
end
provider_capability("docker", "has_communicator") do
require_relative "cap/has_communicator"
Cap::HasCommunicator
end
protected
def self.init!

View File

@ -2697,7 +2697,7 @@ en:
missing_provisioner_name: |-
Could not find provisioner name `%{name}` defined for machine `%{machine_name}` to run provisioner "%{provisioner_name}" `%{action}`.
wrong_type: |-
Provisioner option `%{opt}` is not set as a valid type. Must be a string.
Provisioner option `%{opt}` is not set as a valid type. Must be a %{type}.
chef:
chef_not_detected: |-
The chef binary (either `chef-solo` or `chef-client`) was not found on

View File

@ -133,6 +133,10 @@ en:
Stopping container...
container_ready: |-
Container started and ready for use!
not_provisioning: |-
The following provisioners require a communicator, though none is available (this container does not support SSH).
Not running the following provisioners:
- %{provisioners}
status:
host_state_unknown: |-

View File

@ -441,15 +441,18 @@ describe VagrantPlugins::Kernel_V2::VMConfig do
it "stores the provisioners" do
subject.provision("shell", inline: "foo")
subject.provision("shell", inline: "bar", run: "always") { |s| s.path = "baz" }
subject.provision("shell", inline: "foo", communicator_required: false)
subject.finalize!
r = subject.provisioners
expect(r.length).to eql(2)
expect(r.length).to eql(3)
expect(r[0].run).to be_nil
expect(r[0].config.inline).to eql("foo")
expect(r[1].config.inline).to eql("bar")
expect(r[1].config.path).to eql("baz")
expect(r[1].run).to eql(:always)
expect(r[1].communicator_required).to eql(true)
expect(r[2].communicator_required).to eql(false)
end
it "allows provisioner settings to be overridden" do

View File

@ -0,0 +1,66 @@
require File.expand_path("../../../../base", __FILE__)
describe Vagrant::Action::Builtin::HasProvisioner do
include_context "unit"
let(:provisioner_one) { double("provisioner_one") }
let(:provisioner_two) { double("provisioner_two") }
let(:provisioners) { [provisioner_one, provisioner_two] }
let(:machine) { double("machine") }
let(:ui) {double("ui") }
let(:env) {{ machine: machine, ui: ui, root_path: Pathname.new(".") }}
let(:app) { lambda { |*args| }}
subject { described_class.new(app, env) }
describe "#call" do
before do
allow(provisioner_one).to receive(:communicator_required).and_return(true)
allow(provisioner_one).to receive(:name)
allow(provisioner_one).to receive(:type)
allow(provisioner_two).to receive(:communicator_required).and_return(false)
allow(provisioner_two).to receive(:name)
allow(provisioner_two).to receive(:type)
allow(machine).to receive_message_chain(:config, :vm, :provisioners).and_return(provisioners)
end
context "provider has capability :has_communicator" do
before do
allow(machine).to receive_message_chain(:provider, :capability?).with(:has_communicator).and_return(true)
end
it "does not skip any provisioners if provider has ssh" do
allow(machine).to receive_message_chain(:provider, :capability).with(:has_communicator).and_return(true)
expect(provisioner_one).to_not receive(:communicator_required)
expect(provisioner_two).to_not receive(:communicator_required)
subject.call(env)
expect(env[:skip]).to eq([])
end
it "skips provisioners that require a communicator if provider does not have ssh" do
allow(machine).to receive_message_chain(:provider, :capability).with(:has_communicator).and_return(false)
expect(provisioner_one).to receive(:communicator_required)
expect(provisioner_two).to receive(:communicator_required)
expect(provisioner_one).to receive(:run=).with(:never)
subject.call(env)
expect(env[:skip]).to eq([provisioner_one])
end
end
context "provider does not have capability :has_communicator" do
before do
allow(machine).to receive_message_chain(:provider, :capability?).with(:has_communicator).and_return(false)
end
it "does not skip any provisioners" do
expect(provisioner_one).to_not receive(:communicator_required)
expect(provisioner_two).to_not receive(:communicator_required)
subject.call(env)
expect(env[:skip]).to eq([])
end
end
end
end

View File

@ -35,6 +35,11 @@ option is what type a provisioner is:
every root provisioner, or before all provisioners respectively.
**Note**: This option is currently experimental, so it needs to be explicitly
enabled to work. More info can be found [here](/docs/experimental).
* `communicator_required` (boolean) - Specifies the machine must be accessible by
Vagrant in order to run the provisioner. If set to true, the provisioner will
only run if Vagrant can establish communication with the guest. If set to false
the provisioner will run regardless of Vagrant's ability to communicate with the
guest. Defaults to true.
More information about how to use `before` and `after` options can be read [below](#dependency-provisioners).