terraform/internal/experiments/experiment.go

102 lines
4.1 KiB
Go
Raw Normal View History

experiments: a mechanism for opt-in experimental language features Traditionally we've preferred to release new language features in major releases only, because we can then use the beta cycle to gather feedback on the feature and learn about any usability challenges or other situations we didn't consider during our design in time to make those changes before inclusion in a stable release. This "experiments" feature is intended to decouple the feedback cycle for new features from the major release rhythm, and thus allow us to release new features in minor releases by first releasing them as experimental for a minor release or two, adjust for any feedback gathered during that period, and then finally remove the experiment gate and enable the feature for everyone. The intended model here is that anything behind an experiment gate is subject to breaking changes even in patch releases, and so any module using these experimental features will be broken by a future Terraform upgrade. The behavior implemented here is: - Recognize a new "experiments" setting in the "terraform" block which allows module authors to explicitly opt in to experimental features. terraform { experiments = [resource_for_each] } - Generate a warning whenever loading a module that has experiments enabled, to avoid accidentally depending on experimental features and thus risking unexpected breakage on next Terraform upgrade. - We check the enabled experiments against the configuration at module load time, which means that experiments are scoped to a particular module. Enabling an experiment in one module does not automatically enable it in any other module. This experiments mechanism is itself an experiment, and so I'd like to use the resource for_each feature to trial it. Because any configuration using experiments is subject to breaking changes, we are free to adjust this experiments feature in future releases as we see fit, but once for_each is shipped without an experiment gate we'll be blocked from making significant changes to it until the next major release at least.
2019-07-10 21:37:11 +02:00
package experiments
// Experiment represents a particular experiment, which can be activated
// independently of all other experiments.
type Experiment string
// All active and defunct experiments must be represented by constants whose
// internal string values are unique.
//
// Each of these declared constants must also be registered as either a
// current or a defunct experiment in the init() function below.
//
// Each experiment is represented by a string that must be a valid HCL
// identifier so that it can be specified in configuration.
const (
VariableValidation = Experiment("variable_validation")
ModuleVariableOptionalAttrs = Experiment("module_variable_optional_attrs")
SuppressProviderSensitiveAttrs = Experiment("provider_sensitive_attrs")
ConfigDrivenMove = Experiment("config_driven_move")
PreconditionsPostconditions = Experiment("preconditions_postconditions")
experiments: a mechanism for opt-in experimental language features Traditionally we've preferred to release new language features in major releases only, because we can then use the beta cycle to gather feedback on the feature and learn about any usability challenges or other situations we didn't consider during our design in time to make those changes before inclusion in a stable release. This "experiments" feature is intended to decouple the feedback cycle for new features from the major release rhythm, and thus allow us to release new features in minor releases by first releasing them as experimental for a minor release or two, adjust for any feedback gathered during that period, and then finally remove the experiment gate and enable the feature for everyone. The intended model here is that anything behind an experiment gate is subject to breaking changes even in patch releases, and so any module using these experimental features will be broken by a future Terraform upgrade. The behavior implemented here is: - Recognize a new "experiments" setting in the "terraform" block which allows module authors to explicitly opt in to experimental features. terraform { experiments = [resource_for_each] } - Generate a warning whenever loading a module that has experiments enabled, to avoid accidentally depending on experimental features and thus risking unexpected breakage on next Terraform upgrade. - We check the enabled experiments against the configuration at module load time, which means that experiments are scoped to a particular module. Enabling an experiment in one module does not automatically enable it in any other module. This experiments mechanism is itself an experiment, and so I'd like to use the resource for_each feature to trial it. Because any configuration using experiments is subject to breaking changes, we are free to adjust this experiments feature in future releases as we see fit, but once for_each is shipped without an experiment gate we'll be blocked from making significant changes to it until the next major release at least.
2019-07-10 21:37:11 +02:00
)
func init() {
// Each experiment constant defined above must be registered here as either
// a current or a concluded experiment.
registerConcludedExperiment(VariableValidation, "Custom variable validation can now be used by default, without enabling an experiment.")
registerConcludedExperiment(SuppressProviderSensitiveAttrs, "Provider-defined sensitive attributes are now redacted by default, without enabling an experiment.")
registerConcludedExperiment(ConfigDrivenMove, "Declarations of moved resource instances using \"moved\" blocks can now be used by default, without enabling an experiment.")
registerCurrentExperiment(ModuleVariableOptionalAttrs)
registerCurrentExperiment(PreconditionsPostconditions)
experiments: a mechanism for opt-in experimental language features Traditionally we've preferred to release new language features in major releases only, because we can then use the beta cycle to gather feedback on the feature and learn about any usability challenges or other situations we didn't consider during our design in time to make those changes before inclusion in a stable release. This "experiments" feature is intended to decouple the feedback cycle for new features from the major release rhythm, and thus allow us to release new features in minor releases by first releasing them as experimental for a minor release or two, adjust for any feedback gathered during that period, and then finally remove the experiment gate and enable the feature for everyone. The intended model here is that anything behind an experiment gate is subject to breaking changes even in patch releases, and so any module using these experimental features will be broken by a future Terraform upgrade. The behavior implemented here is: - Recognize a new "experiments" setting in the "terraform" block which allows module authors to explicitly opt in to experimental features. terraform { experiments = [resource_for_each] } - Generate a warning whenever loading a module that has experiments enabled, to avoid accidentally depending on experimental features and thus risking unexpected breakage on next Terraform upgrade. - We check the enabled experiments against the configuration at module load time, which means that experiments are scoped to a particular module. Enabling an experiment in one module does not automatically enable it in any other module. This experiments mechanism is itself an experiment, and so I'd like to use the resource for_each feature to trial it. Because any configuration using experiments is subject to breaking changes, we are free to adjust this experiments feature in future releases as we see fit, but once for_each is shipped without an experiment gate we'll be blocked from making significant changes to it until the next major release at least.
2019-07-10 21:37:11 +02:00
}
// GetCurrent takes an experiment name and returns the experiment value
// representing that expression if and only if it is a current experiment.
//
// If the selected experiment is concluded, GetCurrent will return an
// error of type ConcludedError whose message hopefully includes some guidance
// for users of the experiment on how to migrate to a stable feature that
// succeeded it.
//
// If the selected experiment is not known at all, GetCurrent will return an
// error of type UnavailableError.
func GetCurrent(name string) (Experiment, error) {
exp := Experiment(name)
if currentExperiments.Has(exp) {
return exp, nil
}
if msg, concluded := concludedExperiments[exp]; concluded {
return Experiment(""), ConcludedError{ExperimentName: name, Message: msg}
}
return Experiment(""), UnavailableError{ExperimentName: name}
}
// Keyword returns the keyword that would be used to activate this experiment
// in the configuration.
func (e Experiment) Keyword() string {
return string(e)
}
// IsCurrent returns true if the receiver is considered a currently-selectable
// experiment.
func (e Experiment) IsCurrent() bool {
return currentExperiments.Has(e)
}
// IsConcluded returns true if the receiver is a concluded experiment.
func (e Experiment) IsConcluded() bool {
_, exists := concludedExperiments[e]
return exists
}
// currentExperiments are those which are available to activate in the current
// version of Terraform.
//
// Members of this set are registered in the init function above.
var currentExperiments = make(Set)
// concludedExperiments are those which were available to activate in an earlier
// version of Terraform but are no longer available, either because the feature
// in question has been implemented or because the experiment failed and the
// feature was abandoned. Each experiment maps to a message describing the
// outcome, so we can give users feedback about what they might do in modules
// using concluded experiments.
//
// After an experiment has been concluded for a whole major release span it can
// be removed, since we expect users to perform upgrades one major release at
// at time without skipping and thus they will see the concludedness error
// message as they upgrade through a prior major version.
//
// Members of this map are registered in the init function above.
var concludedExperiments = make(map[Experiment]string)
func registerCurrentExperiment(exp Experiment) {
currentExperiments.Add(exp)
}
func registerConcludedExperiment(exp Experiment, message string) {
concludedExperiments[exp] = message
}