pushes/local-exec: add args config

This commit is contained in:
Hasyimi Bahrudin 2015-12-10 15:58:02 +08:00
parent 25ff027b08
commit 77031a79b7
6 changed files with 159 additions and 13 deletions

View File

@ -10,14 +10,24 @@ module VagrantPlugins
# @return [String]
attr_accessor :inline
# The arguments to provide when executing the script.
# @return [Array<String>]
attr_accessor :args
def initialize
@script = UNSET_VALUE
@inline = UNSET_VALUE
@args = UNSET_VALUE
end
def finalize!
@script = nil if @script == 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
def validate(machine)
@ -33,6 +43,10 @@ module VagrantPlugins
errors << I18n.t("local_exec_push.errors.cannot_specify_script_and_inline")
end
if !args_valid?
errors << I18n.t("local_exec_push.errors.args_bad_type")
end
{ "Local Exec push" => errors }
end
@ -43,6 +57,21 @@ module VagrantPlugins
def missing?(obj)
obj.to_s.strip.empty?
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

View File

@ -20,3 +20,4 @@ en:
config.push.define "local-exec" do |push|
push.%{attribute} = "..."
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)
def push
if config.inline
execute_inline!(config.inline)
execute_inline!(config.inline, config.args)
else
execute_script!(config.script)
execute_script!(config.script, config.args)
end
end
# 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.write(inline)
script.rewind
script.close
execute_script!(script.path)
execute_script!(script.path, args)
ensure
if script
script.close
@ -31,10 +31,18 @@ module VagrantPlugins
end
# 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)
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
# Execute the script, raising an exception if it fails.
@ -48,6 +56,11 @@ module VagrantPlugins
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).
def execute_exec!(*cmd)
Vagrant::Util::SafeExec.exec(cmd[0], *cmd[1..-1])

View File

@ -26,6 +26,13 @@ describe VagrantPlugins::LocalExecPush::Config do
end
end
describe "#args" do
it "defaults to nil" do
subject.finalize!
expect(subject.args).to be(nil)
end
end
describe "#validate" do
before do
allow(machine).to receive(:env)
@ -57,6 +64,42 @@ describe VagrantPlugins::LocalExecPush::Config do
it "does not return an error" do
expect(errors).to be_empty
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
@ -69,6 +112,42 @@ describe VagrantPlugins::LocalExecPush::Config do
it "does not return an error" do
expect(errors).to be_empty
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
context "when inline is not present" do

View File

@ -15,6 +15,7 @@ describe VagrantPlugins::LocalExecPush::Push do
double("config",
script: nil,
inline: nil,
args: "some args",
)
end
@ -37,7 +38,7 @@ describe VagrantPlugins::LocalExecPush::Push do
it "executes the inline script" do
expect(subject).to receive(:execute_inline!)
.with(config.inline)
.with(config.inline, config.args)
subject.push
end
end
@ -47,7 +48,7 @@ describe VagrantPlugins::LocalExecPush::Push do
it "executes the script" do
expect(subject).to receive(:execute_script!)
.with(config.script)
.with(config.script, config.args)
subject.push
end
end
@ -58,12 +59,12 @@ describe VagrantPlugins::LocalExecPush::Push do
it "writes the script to a tempfile" do
expect(Tempfile).to receive(:new).and_call_original
subject.execute_inline!("echo")
subject.execute_inline!("echo", config.args)
end
it "executes the script" do
expect(subject).to receive(:execute_script!)
subject.execute_inline!("echo")
subject.execute_inline!("echo", config.args)
end
end
@ -76,19 +77,33 @@ describe VagrantPlugins::LocalExecPush::Push do
it "expands the path relative to the machine root" do
expect(subject).to receive(:execute!)
.with(File.expand_path("foo.sh", env.root_path))
subject.execute_script!("./foo.sh")
subject.execute_script!("./foo.sh", nil)
end
it "makes the file executable" do
expect(FileUtils).to receive(:chmod)
.with("+x", File.expand_path("foo.sh", env.root_path))
subject.execute_script!("./foo.sh")
subject.execute_script!("./foo.sh", config.args)
end
it "calls execute!" do
expect(subject).to receive(:execute!)
.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

View File

@ -27,6 +27,11 @@ options:
execute. Vagrant will attempt to convert this script to an executable, but an
exception will be raised if that fails.
- `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
a single push definition.
@ -70,3 +75,7 @@ And then invoke the push with Vagrant:
```shell
$ vagrant push
```
### Script Arguments
Refer to [Shell Provisioner](/v2/provisioning/shell.html).