From 612eeb22654ebecacc6e367773cef7603afd8eec Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Fri, 14 Nov 2014 15:51:35 -0500 Subject: [PATCH] Add local-exec push --- plugins/pushes/local-exec/config.rb | 37 +++++++++ plugins/pushes/local-exec/errors.rb | 13 ++++ plugins/pushes/local-exec/locales/en.yml | 18 +++++ plugins/pushes/local-exec/plugin.rb | 33 ++++++++ plugins/pushes/local-exec/push.rb | 28 +++++++ .../plugins/pushes/local-exec/config_test.rb | 45 +++++++++++ .../plugins/pushes/local-exec/push_test.rb | 78 +++++++++++++++++++ 7 files changed, 252 insertions(+) create mode 100644 plugins/pushes/local-exec/config.rb create mode 100644 plugins/pushes/local-exec/errors.rb create mode 100644 plugins/pushes/local-exec/locales/en.yml create mode 100644 plugins/pushes/local-exec/plugin.rb create mode 100644 plugins/pushes/local-exec/push.rb create mode 100644 test/unit/plugins/pushes/local-exec/config_test.rb create mode 100644 test/unit/plugins/pushes/local-exec/push_test.rb diff --git a/plugins/pushes/local-exec/config.rb b/plugins/pushes/local-exec/config.rb new file mode 100644 index 000000000..eb06a72fe --- /dev/null +++ b/plugins/pushes/local-exec/config.rb @@ -0,0 +1,37 @@ +module VagrantPlugins + module LocalExecPush + class Config < Vagrant.plugin("2", :config) + # The command (as a string) to execute. + # @return [String] + attr_accessor :command + + def initialize + @command = UNSET_VALUE + end + + def finalize! + @command = nil if @command == UNSET_VALUE + end + + def validate(machine) + errors = _detected_errors + + if missing?(@command) + errors << I18n.t("local_exec_push.errors.missing_attribute", + attribute: "command", + ) + end + + { "Local Exec push" => errors } + end + + private + + # Determine if the given string is "missing" (blank) + # @return [true, false] + def missing?(obj) + obj.to_s.strip.empty? + end + end + end +end diff --git a/plugins/pushes/local-exec/errors.rb b/plugins/pushes/local-exec/errors.rb new file mode 100644 index 000000000..5e5b71cca --- /dev/null +++ b/plugins/pushes/local-exec/errors.rb @@ -0,0 +1,13 @@ +module VagrantPlugins + module LocalExecPush + module Errors + class Error < Vagrant::Errors::VagrantError + error_namespace("local_exec_push.errors") + end + + class CommandFailed < Error + error_key(:command_failed) + end + end + end +end diff --git a/plugins/pushes/local-exec/locales/en.yml b/plugins/pushes/local-exec/locales/en.yml new file mode 100644 index 000000000..bd219c8ff --- /dev/null +++ b/plugins/pushes/local-exec/locales/en.yml @@ -0,0 +1,18 @@ +en: + local_exec_push: + errors: + command_failed: |- + The following command exited with a non-zero exit status: + + %{cmd} + + stdout: %{stdout} + stderr: %{stderr} + missing_attribute: |- + Missing required attribute '%{attribute}'. The Vagrant Local Exec Push + plugin requires you set this attribute. Please set this attribute in + your Vagrantfile, for example: + + config.push.define "local-exec" do |push| + push.%{attribute} = "..." + end diff --git a/plugins/pushes/local-exec/plugin.rb b/plugins/pushes/local-exec/plugin.rb new file mode 100644 index 000000000..9f8be467b --- /dev/null +++ b/plugins/pushes/local-exec/plugin.rb @@ -0,0 +1,33 @@ +require "vagrant" + +module VagrantPlugins + module LocalExecPush + class Plugin < Vagrant.plugin("2") + name "local-exec" + description <<-DESC + Run a local command or script to push + DESC + + config(:local_exec, :push) do + require File.expand_path("../config", __FILE__) + init! + Config + end + + push(:local_exec) do + require File.expand_path("../push", __FILE__) + init! + Push + end + + protected + + def self.init! + return if defined?(@_init) + I18n.load_path << File.expand_path("../locales/en.yml", __FILE__) + I18n.reload! + @_init = true + end + end + end +end diff --git a/plugins/pushes/local-exec/push.rb b/plugins/pushes/local-exec/push.rb new file mode 100644 index 000000000..cbcab39e7 --- /dev/null +++ b/plugins/pushes/local-exec/push.rb @@ -0,0 +1,28 @@ +require "vagrant/util/subprocess" + +require_relative "errors" + +module VagrantPlugins + module LocalExecPush + class Push < Vagrant.plugin("2", :push) + def push + execute!(config.command) + end + + # Execute the command, raising an exception if it fails. + # @return [Vagrant::Util::Subprocess::Result] + def execute!(*cmd) + result = Vagrant::Util::Subprocess.execute(*cmd) + + if result.exit_code != 0 + raise Errors::CommandFailed, + cmd: cmd.join(" "), + stdout: result.stdout, + stderr: result.stderr + end + + result + end + end + end +end diff --git a/test/unit/plugins/pushes/local-exec/config_test.rb b/test/unit/plugins/pushes/local-exec/config_test.rb new file mode 100644 index 000000000..84cc9d7b4 --- /dev/null +++ b/test/unit/plugins/pushes/local-exec/config_test.rb @@ -0,0 +1,45 @@ +require_relative "../../../base" + +require Vagrant.source_root.join("plugins/pushes/local-exec/config") + +describe VagrantPlugins::LocalExecPush::Config do + include_context "unit" + + before(:all) do + I18n.load_path << Vagrant.source_root.join("plugins/pushes/local-exec/locales/en.yml") + I18n.reload! + end + + let(:machine) { double("machine") } + + describe "#command" do + it "defaults to nil" do + subject.finalize! + expect(subject.command).to be(nil) + end + end + + describe "#validate" do + before do + allow(machine).to receive(:env) + .and_return(double("env", + root_path: "", + )) + + subject.command = "echo" + end + + let(:result) { subject.validate(machine) } + let(:errors) { result["Local Exec push"] } + + context "when the command is missing" do + it "returns an error" do + subject.command = "" + subject.finalize! + expect(errors).to include(I18n.t("local_exec_push.errors.missing_attribute", + attribute: "command", + )) + end + end + end +end diff --git a/test/unit/plugins/pushes/local-exec/push_test.rb b/test/unit/plugins/pushes/local-exec/push_test.rb new file mode 100644 index 000000000..23f487546 --- /dev/null +++ b/test/unit/plugins/pushes/local-exec/push_test.rb @@ -0,0 +1,78 @@ +require_relative "../../../base" + +require Vagrant.source_root.join("plugins/pushes/local-exec/push") + +describe VagrantPlugins::LocalExecPush::Push do + include_context "unit" + + before(:all) do + I18n.load_path << Vagrant.source_root.join("plugins/pushes/local-exec/locales/en.yml") + I18n.reload! + end + + let(:env) { isolated_environment } + let(:config) do + double("config", + command: "echo", + ) + end + + subject { described_class.new(env, config) } + + describe "#push" do + before do + allow(subject).to receive(:execute!) + end + + it "executes the command" do + expect(subject).to receive(:execute!) + .with(config.command) + subject.push + end + end + + describe "#execute!" do + let(:exit_code) { 0 } + let(:stdout) { "This is the output" } + let(:stderr) { "This is the errput" } + + let(:process) do + double("process", + exit_code: exit_code, + stdout: stdout, + stderr: stderr, + ) + end + + before do + allow(Vagrant::Util::Subprocess).to receive(:execute) + .and_return(process) + end + + it "creates a subprocess" do + expect(Vagrant::Util::Subprocess).to receive(:execute) + expect { subject.execute! }.to_not raise_error + end + + it "returns the resulting process" do + expect(subject.execute!).to be(process) + end + + context "when the exit code is non-zero" do + let(:exit_code) { 1 } + + it "raises an exception" do + klass = VagrantPlugins::LocalExecPush::Errors::CommandFailed + cmd = ["foo", "bar"] + + expect { subject.execute!(*cmd) }.to raise_error(klass) { |error| + expect(error.message).to eq(I18n.t("local_exec_push.errors.command_failed", + cmd: cmd.join(" "), + stdout: stdout, + stderr: stderr, + )) + } + end + end + end +end