Merge pull request #10485 from briancain/introduce-experimental-feature-flag

Add experimental flag to guard development features
This commit is contained in:
Brian Cain 2018-12-07 16:25:10 -08:00 committed by GitHub
commit 2011e213c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 215 additions and 0 deletions

View File

@ -89,6 +89,7 @@ begin
require 'vagrant/bundler'
require 'vagrant/cli'
require 'vagrant/util/platform'
require 'vagrant/util/experimental'
# Schedule the cleanup of things
at_exit(&Vagrant::Bundler.instance.method(:deinit))
@ -159,6 +160,23 @@ 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 Vagrant::Util::Experimental.enabled?
experimental = Vagrant::Util::Experimental.features_requested
ui = Vagrant::UI::Prefixed.new(env.ui, "vagrant")
logger.debug("Experimental flag is enabled")
if Vagrant::Util::Experimental.global_enabled?
ui.warn(I18n.t("vagrant.general.experimental.all"), bold: true, prefix: true, channel: :error)
else
ui.warn(I18n.t("vagrant.general.experimental.features", features: experimental.join(", ")), bold: true, prefix: true, channel: :error)
end
end
begin
# Execute the CLI interface, and exit with the proper error code
exit_status = env.cli(argv)

View File

@ -0,0 +1,76 @@
module Vagrant
module Util
class Experimental
class << self
# A method for determining if the experimental flag has been enabled with
# any features
#
# @return [Boolean]
def enabled?
if !defined?(@_experimental)
experimental = features_requested
if experimental.size >= 1 && experimental.first != "0"
@_experimental = true
else
@_experimental = false
end
end
@_experimental
end
# A method for determining if all experimental features have been enabled
# by either a global enabled value "1" or all features explicitly enabled.
#
# @return [Boolean]
def global_enabled?
if !defined?(@_global_enabled)
experimental = features_requested
if experimental.size == 1 && experimental.first == "1"
@_global_enabled = true
else
@_global_enabled = false
end
end
@_global_enabled
end
# A method for Vagrant internals to determine if a given feature
# has been abled by the user, is a valid feature flag and can be used.
#
# @param [String] feature
# @return [Boolean] - A hash containing the original array and if it is valid
def feature_enabled?(feature)
experimental = features_requested
feature = feature.to_s
return global_enabled? || experimental.include?(feature)
end
# Returns the features requested for the experimental flag
#
# @return [Array] - Returns an array of requested experimental features
def features_requested
if !defined?(@_requested_features)
@_requested_features = ENV["VAGRANT_EXPERIMENTAL"].to_s.downcase.split(',')
end
@_requested_features
end
# A function to guard experimental blocks of code from being executed
#
# @param [Array] features - Array of features to guard a method with
# @param [Block] block - Block of ruby code to be guarded against
def guard_with(*features, &block)
yield if block_given? && features.any? {|f| feature_enabled?(f)}
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

View File

@ -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

View File

@ -0,0 +1,109 @@
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 "#global_enabled?" do
it "returns true if enabled with '1'" do
allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return("1")
expect(subject.global_enabled?).to eq(true)
end
it "returns false if enabled with a partial list of features" do
allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return("list,of,features")
expect(subject.global_enabled?).to eq(false)
end
it "returns false if disabled" do
allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return("0")
expect(subject.global_enabled?).to eq(false)
end
it "returns false if not set" do
allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return(nil)
expect(subject.global_enabled?).to eq(false)
end
end
describe "#feature_enabled?" do
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 and the request is a symbol" 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 is set but does not contain feature requested" do
allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return("fake_feature")
expect(subject.feature_enabled?("secret_feature")).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
describe "#features_requested" do
it "returns an array of requested features" do
allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return("secret_feature,other_secret")
expect(subject.features_requested).to eq(["secret_feature","other_secret"])
end
end
describe "#guard_with" do
it "does not execute the block if the feature is not requested" do
allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return(nil)
expect{|b| subject.guard_with("secret_feature", &b) }.not_to yield_control
end
it "executes the block if the feature is valid and requested" do
allow(ENV).to receive(:[]).with("VAGRANT_EXPERIMENTAL").and_return("secret_feature,other_secret")
expect{|b| subject.guard_with("secret_feature", &b) }.to yield_control
end
end
end