diff --git a/bin/vagrant b/bin/vagrant index 7372a726d..4ab3f013d 100755 --- a/bin/vagrant +++ b/bin/vagrant @@ -159,6 +159,31 @@ begin env.ui.warn(I18n.t("vagrant.general.not_in_installer") + "\n", prefix: false) end + # Acceptable experimental flag values include: + # + # Unset - Disables experimental features + # 0 - Disables experimental features + # 1 - Enables all features + # String - Enables one or more features, separated by commas + if ENV["VAGRANT_EXPERIMENTAL"] + experimental = ENV["VAGRANT_EXPERIMENTAL"].to_s + if experimental == "0" + logger.debug("Experimental flag is set but disabled") + else + ui = Vagrant::UI::Prefixed.new(env.ui, "vagrant") + if experimental != "1" + logger.debug("Experimental flag is enabled") + features = experimental.split(',') + ui.warn(I18n.t("vagrant.general.experimental.features", features: features.join(", ")), bold: true, prefix: true, channel: :error) + elsif experimental == "1" + logger.debug("Experimental flag is enabled") + ui.warn(I18n.t("vagrant.general.experimental.all"), bold: true, prefix: true, channel: :error) + else + logger.warn("Experimental flag is set to an unsupported value") + end + end + end + begin # Execute the CLI interface, and exit with the proper error code exit_status = env.cli(argv) diff --git a/lib/vagrant/util/experimental.rb b/lib/vagrant/util/experimental.rb new file mode 100644 index 000000000..732a0643c --- /dev/null +++ b/lib/vagrant/util/experimental.rb @@ -0,0 +1,48 @@ +module Vagrant + module Util + class Experimental + VALID_FEATURES = [].freeze + class << self + + # A method for determining if the experimental flag has been enabled + # + # @return [Boolean] + def enabled? + if !defined?(@_experimental) + experimental = ENV["VAGRANT_EXPERIMENTAL"].to_s + if experimental != "0" && !experimental.empty? + @_experimental = true + else + @_experimental = false + end + end + @_experimental + end + + # A method for Vagrant internals to determine if a given feature + # has been abled and can be used. + # + # @param [String] - An array of strings of features to check against + # @return [Boolean] - A hash containing the original array and if it is valid + def feature_enabled?(feature) + experimental = ENV["VAGRANT_EXPERIMENTAL"].to_s.downcase + if experimental == "1" + return true + elsif VALID_FEATURES.include?(feature) && + experimental.split(',').include?(feature) + return true + else + return false + end + end + + # @private + # Reset the cached values for platform. This is not considered a public + # API and should only be used for testing. + def reset! + instance_variables.each(&method(:remove_instance_variable)) + end + end + end + end +end diff --git a/templates/locales/en.yml b/templates/locales/en.yml index e6967d2a0..3890182b6 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -393,6 +393,18 @@ en: shown below. %{output} + experimental: + all: |- + You have enabled the experimental flag with all features enabled. + Please use with caution, as some of the features may not be fully + functional yet. + features: |- + You have requested to enabled the experimental flag with the following features: + + Features: %{features} + + Please use with caution, as some of the features may not be fully + functional yet. not_in_installer: |- You appear to be running Vagrant outside of the official installers. Note that the installers are what ensure that Vagrant has all required diff --git a/test/unit/vagrant/util/experimental_test.rb b/test/unit/vagrant/util/experimental_test.rb new file mode 100644 index 000000000..222e19723 --- /dev/null +++ b/test/unit/vagrant/util/experimental_test.rb @@ -0,0 +1,67 @@ +require File.expand_path("../../../base", __FILE__) + +require "vagrant/util/experimental" + +describe Vagrant::Util::Experimental do + include_context "unit" + before(:each) { described_class.reset! } + subject { described_class } + + describe "#enabled?" do + it "returns true if enabled with '1'" do + allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return("1") + expect(subject.enabled?).to eq(true) + end + + it "returns true if enabled with a list of features" do + allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return("list,of,features") + expect(subject.enabled?).to eq(true) + end + + it "returns false if disabled" do + allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return("0") + expect(subject.enabled?).to eq(false) + end + + it "returns false if not set" do + allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return(nil) + expect(subject.enabled?).to eq(false) + end + end + + describe "#feature_enabled?" do + before(:each) do + stub_const("Vagrant::Util::Experimental::VALID_FEATURES", ["secret_feature"]) + end + + it "returns true if flag set to 1" do + allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return("1") + expect(subject.feature_enabled?("anything")).to eq(true) + end + + it "returns true if flag contains feature requested" do + allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return("secret_feature") + expect(subject.feature_enabled?("secret_feature")).to eq(true) + end + + it "returns true if flag contains feature requested with other features 'enabled'" do + allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return("secret_feature,other_secret") + expect(subject.feature_enabled?("secret_feature")).to eq(true) + end + + it "returns false if flag does not contain feature requested" do + allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return("secret_feature") + expect(subject.feature_enabled?("anything")).to eq(false) + end + + it "returns false if flag set to 0" do + allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return("0") + expect(subject.feature_enabled?("anything")).to eq(false) + end + + it "returns false if flag is not set" do + allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return(nil) + expect(subject.feature_enabled?("anything")).to eq(false) + end + end +end