Merge pull request #6661 from hasyimibhar/feature/push-local-exec-args

pushes/local-exec: add args config
This commit is contained in:
Seth Vargo 2015-12-10 11:26:09 -05:00
commit e92a214803
6 changed files with 159 additions and 13 deletions

View File

@ -10,14 +10,24 @@ module VagrantPlugins
# @return [String] # @return [String]
attr_accessor :inline attr_accessor :inline
# The arguments to provide when executing the script.
# @return [Array<String>]
attr_accessor :args
def initialize def initialize
@script = UNSET_VALUE @script = UNSET_VALUE
@inline = UNSET_VALUE @inline = UNSET_VALUE
@args = UNSET_VALUE
end end
def finalize! def finalize!
@script = nil if @script == UNSET_VALUE @script = nil if @script == UNSET_VALUE
@inline = nil if @inline == UNSET_VALUE @inline = nil if @inline == UNSET_VALUE
@args = nil if @args == UNSET_VALUE
if @args && args_valid?
@args = @args.is_a?(Array) ? @args.map { |a| a.to_s } : @args.to_s
end
end end
def validate(machine) def validate(machine)
@ -33,6 +43,10 @@ module VagrantPlugins
errors << I18n.t("local_exec_push.errors.cannot_specify_script_and_inline") errors << I18n.t("local_exec_push.errors.cannot_specify_script_and_inline")
end end
if !args_valid?
errors << I18n.t("local_exec_push.errors.args_bad_type")
end
{ "Local Exec push" => errors } { "Local Exec push" => errors }
end end
@ -43,6 +57,21 @@ module VagrantPlugins
def missing?(obj) def missing?(obj)
obj.to_s.strip.empty? obj.to_s.strip.empty?
end end
# Args are optional, but if they're provided we only support them as a
# string or as an array.
def args_valid?
return true if !args
return true if args.is_a?(String)
return true if args.is_a?(Fixnum)
if args.is_a?(Array)
args.each do |a|
return false if !a.kind_of?(String) && !a.kind_of?(Fixnum)
end
return true
end
end
end end
end end
end end

View File

@ -20,3 +20,4 @@ en:
config.push.define "local-exec" do |push| config.push.define "local-exec" do |push|
push.%{attribute} = "..." push.%{attribute} = "..."
end end
args_bad_type: "Local-exec push `args` must be a string or array."

View File

@ -9,20 +9,20 @@ module VagrantPlugins
class Push < Vagrant.plugin("2", :push) class Push < Vagrant.plugin("2", :push)
def push def push
if config.inline if config.inline
execute_inline!(config.inline) execute_inline!(config.inline, config.args)
else else
execute_script!(config.script) execute_script!(config.script, config.args)
end end
end end
# Execute the inline script by writing it to a tempfile and executing. # Execute the inline script by writing it to a tempfile and executing.
def execute_inline!(inline) def execute_inline!(inline, args)
script = Tempfile.new(["vagrant-local-exec-script", ".sh"]) script = Tempfile.new(["vagrant-local-exec-script", ".sh"])
script.write(inline) script.write(inline)
script.rewind script.rewind
script.close script.close
execute_script!(script.path) execute_script!(script.path, args)
ensure ensure
if script if script
script.close script.close
@ -31,10 +31,18 @@ module VagrantPlugins
end end
# Execute the script, expanding the path relative to the current env root. # Execute the script, expanding the path relative to the current env root.
def execute_script!(path) def execute_script!(path, args)
path = File.expand_path(path, env.root_path) path = File.expand_path(path, env.root_path)
FileUtils.chmod("+x", path) FileUtils.chmod("+x", path)
execute!(path)
if args.is_a?(String)
args = " #{args.to_s}"
elsif args.is_a?(Array)
args = args.map { |a| quote_and_escape(a) }
args = " #{args.join(" ")}"
end
execute!("#{path}#{args}")
end end
# Execute the script, raising an exception if it fails. # Execute the script, raising an exception if it fails.
@ -48,6 +56,11 @@ module VagrantPlugins
private private
# Quote and escape strings for shell execution, thanks to Capistrano.
def quote_and_escape(text, quote = '"')
"#{quote}#{text.gsub(/#{quote}/) { |m| "#{m}\\#{m}#{m}" }}#{quote}"
end
# Run the command as exec (unix). # Run the command as exec (unix).
def execute_exec!(*cmd) def execute_exec!(*cmd)
Vagrant::Util::SafeExec.exec(cmd[0], *cmd[1..-1]) Vagrant::Util::SafeExec.exec(cmd[0], *cmd[1..-1])

View File

@ -26,6 +26,13 @@ describe VagrantPlugins::LocalExecPush::Config do
end end
end end
describe "#args" do
it "defaults to nil" do
subject.finalize!
expect(subject.args).to be(nil)
end
end
describe "#validate" do describe "#validate" do
before do before do
allow(machine).to receive(:env) allow(machine).to receive(:env)
@ -57,6 +64,42 @@ describe VagrantPlugins::LocalExecPush::Config do
it "does not return an error" do it "does not return an error" do
expect(errors).to be_empty expect(errors).to be_empty
end end
it "passes with string args" do
subject.args = "a string"
expect(errors).to be_empty
end
it "passes with fixnum args" do
subject.args = 1
expect(errors).to be_empty
end
it "passes with array args" do
subject.args = ["an", "array"]
expect(errors).to be_empty
end
it "returns an error if args is neither a string nor an array" do
neither_array_nor_string = Object.new
subject.args = neither_array_nor_string
expect(errors).to include(
I18n.t("local_exec_push.errors.args_bad_type")
)
end
it "handles scalar array args" do
subject.args = ["string", 1, 2]
expect(errors).to be_empty
end
it "returns an error if args is an array with non-scalar types" do
subject.args = [[1]]
expect(errors).to include(
I18n.t("local_exec_push.errors.args_bad_type")
)
end
end end
end end
@ -69,6 +112,42 @@ describe VagrantPlugins::LocalExecPush::Config do
it "does not return an error" do it "does not return an error" do
expect(errors).to be_empty expect(errors).to be_empty
end end
it "passes with string args" do
subject.args = "a string"
expect(errors).to be_empty
end
it "passes with fixnum args" do
subject.args = 1
expect(errors).to be_empty
end
it "passes with array args" do
subject.args = ["an", "array"]
expect(errors).to be_empty
end
it "returns an error if args is neither a string nor an array" do
neither_array_nor_string = Object.new
subject.args = neither_array_nor_string
expect(errors).to include(
I18n.t("local_exec_push.errors.args_bad_type")
)
end
it "handles scalar array args" do
subject.args = ["string", 1, 2]
expect(errors).to be_empty
end
it "returns an error if args is an array with non-scalar types" do
subject.args = [[1]]
expect(errors).to include(
I18n.t("local_exec_push.errors.args_bad_type")
)
end
end end
context "when inline is not present" do context "when inline is not present" do

View File

@ -15,6 +15,7 @@ describe VagrantPlugins::LocalExecPush::Push do
double("config", double("config",
script: nil, script: nil,
inline: nil, inline: nil,
args: "some args",
) )
end end
@ -37,7 +38,7 @@ describe VagrantPlugins::LocalExecPush::Push do
it "executes the inline script" do it "executes the inline script" do
expect(subject).to receive(:execute_inline!) expect(subject).to receive(:execute_inline!)
.with(config.inline) .with(config.inline, config.args)
subject.push subject.push
end end
end end
@ -47,7 +48,7 @@ describe VagrantPlugins::LocalExecPush::Push do
it "executes the script" do it "executes the script" do
expect(subject).to receive(:execute_script!) expect(subject).to receive(:execute_script!)
.with(config.script) .with(config.script, config.args)
subject.push subject.push
end end
end end
@ -58,12 +59,12 @@ describe VagrantPlugins::LocalExecPush::Push do
it "writes the script to a tempfile" do it "writes the script to a tempfile" do
expect(Tempfile).to receive(:new).and_call_original expect(Tempfile).to receive(:new).and_call_original
subject.execute_inline!("echo") subject.execute_inline!("echo", config.args)
end end
it "executes the script" do it "executes the script" do
expect(subject).to receive(:execute_script!) expect(subject).to receive(:execute_script!)
subject.execute_inline!("echo") subject.execute_inline!("echo", config.args)
end end
end end
@ -76,19 +77,33 @@ describe VagrantPlugins::LocalExecPush::Push do
it "expands the path relative to the machine root" do it "expands the path relative to the machine root" do
expect(subject).to receive(:execute!) expect(subject).to receive(:execute!)
.with(File.expand_path("foo.sh", env.root_path)) .with(File.expand_path("foo.sh", env.root_path))
subject.execute_script!("./foo.sh") subject.execute_script!("./foo.sh", nil)
end end
it "makes the file executable" do it "makes the file executable" do
expect(FileUtils).to receive(:chmod) expect(FileUtils).to receive(:chmod)
.with("+x", File.expand_path("foo.sh", env.root_path)) .with("+x", File.expand_path("foo.sh", env.root_path))
subject.execute_script!("./foo.sh") subject.execute_script!("./foo.sh", config.args)
end end
it "calls execute!" do it "calls execute!" do
expect(subject).to receive(:execute!) expect(subject).to receive(:execute!)
.with(File.expand_path("foo.sh", env.root_path)) .with(File.expand_path("foo.sh", env.root_path))
subject.execute_script!("./foo.sh") subject.execute_script!("./foo.sh", nil)
end
context "when args is given" do
it "passes string args to execute!" do
expect(subject).to receive(:execute!)
.with(File.expand_path("foo.sh", env.root_path) + " " + config.args)
subject.execute_script!("./foo.sh", config.args)
end
it "passes array args as string to execute!" do
expect(subject).to receive(:execute!)
.with(File.expand_path("foo.sh", env.root_path) + " \"one\" \"two\" \"three\"")
subject.execute_script!("./foo.sh", ["one", "two", "three"])
end
end end
end end

View File

@ -27,6 +27,11 @@ options:
execute. Vagrant will attempt to convert this script to an executable, but an execute. Vagrant will attempt to convert this script to an executable, but an
exception will be raised if that fails. exception will be raised if that fails.
- `inline` - The inline script to execute (as a string). - `inline` - The inline script to execute (as a string).
- `args` (string or array) - Optional arguments to pass to the shell script when executing it
as a single string. These arguments must be written as if they were typed
directly on the command line, so be sure to escape characters, quote,
etc. as needed. You may also pass the arguments in using an array. In this
case, Vagrant will handle quoting for you.
Please note - only one of the `script` and `inline` options may be specified in Please note - only one of the `script` and `inline` options may be specified in
a single push definition. a single push definition.
@ -70,3 +75,7 @@ And then invoke the push with Vagrant:
```shell ```shell
$ vagrant push $ vagrant push
``` ```
### Script Arguments
Refer to [Shell Provisioner](/v2/provisioning/shell.html).