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/bundler'
require 'vagrant/cli' require 'vagrant/cli'
require 'vagrant/util/platform' require 'vagrant/util/platform'
require 'vagrant/util/experimental'
# Schedule the cleanup of things # Schedule the cleanup of things
at_exit(&Vagrant::Bundler.instance.method(:deinit)) 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) env.ui.warn(I18n.t("vagrant.general.not_in_installer") + "\n", prefix: false)
end 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 begin
# Execute the CLI interface, and exit with the proper error code # Execute the CLI interface, and exit with the proper error code
exit_status = env.cli(argv) 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. shown below.
%{output} %{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: |- not_in_installer: |-
You appear to be running Vagrant outside of the official installers. You appear to be running Vagrant outside of the official installers.
Note that the installers are what ensure that Vagrant has all required 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