diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index 4f087a103..f89c94773 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -539,6 +539,51 @@ module Vagrant end end + # This executes the push with the given name, raising any exceptions that + # occur. + # + def push(name=nil) + @logger.info("Getting push: #{name}") + + if vagrantfile.pushes.nil? || vagrantfile.pushes.empty? + raise Vagrant::Errors::PushesNotDefined + end + + if name.nil? + if vagrantfile.pushes.length != 1 + raise Vagrant::Errors::PushStrategyNotProvided, + pushes: vagrantfile.pushes + end + name = vagrantfile.pushes.first + else + if !vagrantfile.pushes.include?(name.to_sym) + raise Vagrant::Errors::PushStrategyNotDefined, + name: name, + pushes: vagrantfile.pushes + end + end + + push_registry = Vagrant.plugin("2").manager.pushes + + push_config = vagrantfile.push(name) + push_config.each do |strategy, config_blocks| + plugin, options = push_registry.get(strategy) + + # TODO: What do we do with options? + # options + + if plugin.nil? + raise Vagrant::Errors::PushStrategyNotLoaded, + name: strategy, + pushes: push_registry.keys + end + + # TODO: This should take a plugin configuration, not a list of config + # blocks, or should it? + plugin.new(self, config_blocks).push + end + end + # This returns a machine with the proper provider for this environment. # The machine named by `name` must be in this environment. # diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index 9edbaf194..de002ffed 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -564,6 +564,10 @@ module Vagrant error_key(:push_strategy_not_defined) end + class PushStrategyNotLoaded < VagrantError + error_key(:push_strategy_not_loaded) + end + class PushStrategyNotProvided < VagrantError error_key(:push_strategy_not_provided) end diff --git a/lib/vagrant/vagrantfile.rb b/lib/vagrant/vagrantfile.rb index 011d16351..7467ffbbc 100644 --- a/lib/vagrant/vagrantfile.rb +++ b/lib/vagrant/vagrantfile.rb @@ -247,6 +247,20 @@ module Vagrant nil end + # Returns the list of defined pushes in this Vagrantfile. + # + # @return [Array] + def pushes + @config.push.defined_pushes + end + + # Get the push by the given name. + # + # @return [idk] + def push(name) + @config.push.get_push(name) + end + protected def find_vagrantfile(search_path) diff --git a/plugins/commands/push/command.rb b/plugins/commands/push/command.rb index 6cbe3452e..ec7324b55 100644 --- a/plugins/commands/push/command.rb +++ b/plugins/commands/push/command.rb @@ -17,13 +17,14 @@ module VagrantPlugins argv = parse_options(opts) return if !argv - name, options = argv + name = argv[0] pushes = @env.pushes + # TODO: this logic is 100% duplicated in Enviroment#push - should we + # just not validate here? validate_pushes!(pushes, name) @logger.debug("'push' environment with strategy: `#{name}'") - @env.push(name) 0 @@ -50,7 +51,7 @@ module VagrantPlugins raise Vagrant::Errors::PushStrategyNotProvided, pushes: pushes end else - if !pushes.has_key?(name.to_sym) + if !pushes.key?(name.to_sym) raise Vagrant::Errors::PushStrategyNotDefined, name: name, pushes: pushes diff --git a/plugins/kernel_v2/config/push.rb b/plugins/kernel_v2/config/push.rb new file mode 100644 index 000000000..c5c3a10b0 --- /dev/null +++ b/plugins/kernel_v2/config/push.rb @@ -0,0 +1,99 @@ +require "vagrant" + +module VagrantPlugins + module Kernel_V2 + class PushConfig < Vagrant.plugin("2", :config) + VALID_OPTIONS = [:strategy].freeze + + attr_accessor :name + + def initialize + # Internal state + @__defined_pushes = {} + @__finalized = false + end + + def finalize! + @__finalized = true + end + + # Define a new push in the Vagrantfile with the given name. + # + # @example + # vm.push.define "ftp" + # + # @example + # vm.push.define "ftp" do |s| + # s.host = "..." + # end + # + # @example + # vm.push.define "production", strategy: "docker" do |s| + # # ... + # end + # + # @param [#to_sym] name The name of the this strategy. By default, this + # is also the name of the strategy, but the `:strategy` key can be given + # to customize this behavior + # @param [Hash] options The list of options + # + def define(name, **options, &block) + validate_options!(options) + + name = name.to_sym + strategy = options[:strategy] || name + + @__defined_pushes[name] ||= [] + @__defined_pushes[name] << [strategy.to_sym, block] + end + + # The String representation of this Push. + # + # @return [String] + def to_s + "Push" + end + + # Custom merge method + def merge(other) + super.tap do |result| + other_pushes = other.instance_variable_get(:@__defined_pushes) + new_pushes = @__defined_pushes.dup + + other_pushes.each do |key, tuples| + new_pushes[key] ||= [] + new_pushes[key] += tuples + end + + result.instance_variable_set(:@__defined_pushes, new_pushes) + end + end + + # This returns the list of pushes defined in the Vagrantfile. + # + # @return [Array] + def defined_pushes + raise "Must finalize first!" if !@__finalized + @__defined_pushes.keys + end + + # This returns the compiled push-specific configuration for the given + # provider. + # + # @param [#to_sym] name Name of the push + def get_push(name) + raise "Must finalize first!" if !@__finalized + @__defined_pushes[name.to_sym] + end + + private + + def validate_options!(options) + extra_keys = VALID_OPTIONS - options.keys + if !extra_keys.empty? + raise "Invalid option(s): #{extra_keys.join(", ")}" + end + end + end + end +end diff --git a/plugins/kernel_v2/plugin.rb b/plugins/kernel_v2/plugin.rb index 0904481df..27737854f 100644 --- a/plugins/kernel_v2/plugin.rb +++ b/plugins/kernel_v2/plugin.rb @@ -25,6 +25,11 @@ module VagrantPlugins PackageConfig end + config("push") do + require File.expand_path("../config/push", __FILE__) + PushConfig + end + config("vagrant") do require File.expand_path("../config/vagrant", __FILE__) VagrantConfig diff --git a/templates/locales/en.yml b/templates/locales/en.yml index ce9132fd6..fa3c76414 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -955,12 +955,19 @@ en: end push_strategy_not_defined: |- The push strategy '%{name}' is not defined in the Vagrantfile. Defined - strategies are: + strategy names are: + + %{pushes} + + push_strategy_not_defined: |- + There are no push strategies named '%{name}'. Please make sure you + spelled it correctly. If you are using an external push strategy, you + may need to install a plugin. Loaded push strategies are: %{pushes} push_strategy_not_provided: |- The Vagrantfile defines more than one 'push' strategy. Please specify a - strategy. Defined strategies are: + strategy. Defined strategy names are: %{pushes} package_include_symlink: |- diff --git a/test/unit/plugins/commands/push/command_test.rb b/test/unit/plugins/commands/push/command_test.rb index 34bb111ea..85be81766 100644 --- a/test/unit/plugins/commands/push/command_test.rb +++ b/test/unit/plugins/commands/push/command_test.rb @@ -6,14 +6,6 @@ describe VagrantPlugins::CommandPush::Command do include_context "unit" include_context "command plugin helpers" - def create_registry(items={}) - Vagrant::Registry.new.tap do |registry| - items.each do |k,v| - registry.register(k) { v } - end - end - end - let(:env) do isolated_environment.tap do |env| env.vagrantfile("") @@ -22,7 +14,7 @@ describe VagrantPlugins::CommandPush::Command do end let(:argv) { [] } - let(:pushes) { create_registry } + let(:pushes) { {} } subject { described_class.new(argv, env) } @@ -50,7 +42,7 @@ describe VagrantPlugins::CommandPush::Command do describe "#validate_pushes!" do context "when there are no pushes defined" do - let(:pushes) { create_registry } + let(:pushes) { {} } context "when a strategy is given" do it "raises an exception" do @@ -69,7 +61,7 @@ describe VagrantPlugins::CommandPush::Command do context "when there is one push defined" do let(:noop) { double("noop") } - let(:pushes) { create_registry(noop: noop) } + let(:pushes) { { noop: noop } } context "when a strategy is given" do context "when that strategy is not defined" do @@ -98,7 +90,7 @@ describe VagrantPlugins::CommandPush::Command do context "when there are multiple pushes defined" do let(:noop) { double("noop") } let(:ftp) { double("ftp") } - let(:pushes) { create_registry(noop: noop, ftp: ftp) } + let(:pushes) { { noop: noop, ftp: ftp } } context "when a strategy is given" do context "when that strategy is not defined" do