123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- // Copyright 2025 The frp Authors
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package featuregate
- import (
- "fmt"
- "sort"
- "strings"
- "sync"
- "sync/atomic"
- )
- // Feature represents a feature gate name
- type Feature string
- // FeatureStage represents the maturity level of a feature
- type FeatureStage string
- const (
- // Alpha means the feature is experimental and disabled by default
- Alpha FeatureStage = "ALPHA"
- // Beta means the feature is more stable but still might change and is disabled by default
- Beta FeatureStage = "BETA"
- // GA means the feature is generally available and enabled by default
- GA FeatureStage = ""
- )
- // FeatureSpec describes a feature and its properties
- type FeatureSpec struct {
- // Default is the default enablement state for the feature
- Default bool
- // LockToDefault indicates the feature cannot be changed from its default
- LockToDefault bool
- // Stage indicates the maturity level of the feature
- Stage FeatureStage
- }
- // Define all available features here
- var (
- VirtualNet = Feature("VirtualNet")
- )
- // defaultFeatures defines default features with their specifications
- var defaultFeatures = map[Feature]FeatureSpec{
- // Actual features
- VirtualNet: {Default: false, Stage: Alpha},
- }
- // FeatureGate indicates whether a given feature is enabled or not
- type FeatureGate interface {
- // Enabled returns true if the key is enabled
- Enabled(key Feature) bool
- // KnownFeatures returns a slice of strings describing the known features
- KnownFeatures() []string
- }
- // MutableFeatureGate allows for dynamic feature gate configuration
- type MutableFeatureGate interface {
- FeatureGate
- // SetFromMap sets feature gate values from a map[string]bool
- SetFromMap(m map[string]bool) error
- // Add adds features to the feature gate
- Add(features map[Feature]FeatureSpec) error
- // String returns a string representing the feature gate configuration
- String() string
- }
- // featureGate implements the FeatureGate and MutableFeatureGate interfaces
- type featureGate struct {
- // lock guards writes to known, enabled, and reads/writes of closed
- lock sync.Mutex
- // known holds a map[Feature]FeatureSpec
- known atomic.Value
- // enabled holds a map[Feature]bool
- enabled atomic.Value
- // closed is set to true once the feature gates are considered immutable
- closed bool
- }
- // NewFeatureGate creates a new feature gate with the default features
- func NewFeatureGate() MutableFeatureGate {
- known := map[Feature]FeatureSpec{}
- for k, v := range defaultFeatures {
- known[k] = v
- }
- f := &featureGate{}
- f.known.Store(known)
- f.enabled.Store(map[Feature]bool{})
- return f
- }
- // SetFromMap sets feature gate values from a map[string]bool
- func (f *featureGate) SetFromMap(m map[string]bool) error {
- f.lock.Lock()
- defer f.lock.Unlock()
- // Copy existing state
- known := map[Feature]FeatureSpec{}
- for k, v := range f.known.Load().(map[Feature]FeatureSpec) {
- known[k] = v
- }
- enabled := map[Feature]bool{}
- for k, v := range f.enabled.Load().(map[Feature]bool) {
- enabled[k] = v
- }
- // Apply the new settings
- for k, v := range m {
- k := Feature(k)
- featureSpec, ok := known[k]
- if !ok {
- return fmt.Errorf("unrecognized feature gate: %s", k)
- }
- if featureSpec.LockToDefault && featureSpec.Default != v {
- return fmt.Errorf("cannot set feature gate %v to %v, feature is locked to %v", k, v, featureSpec.Default)
- }
- enabled[k] = v
- }
- // Persist the changes
- f.known.Store(known)
- f.enabled.Store(enabled)
- return nil
- }
- // Add adds features to the feature gate
- func (f *featureGate) Add(features map[Feature]FeatureSpec) error {
- f.lock.Lock()
- defer f.lock.Unlock()
- if f.closed {
- return fmt.Errorf("cannot add feature gates after the feature gate is closed")
- }
- // Copy existing state
- known := map[Feature]FeatureSpec{}
- for k, v := range f.known.Load().(map[Feature]FeatureSpec) {
- known[k] = v
- }
- // Add new features
- for name, spec := range features {
- if existingSpec, found := known[name]; found {
- if existingSpec == spec {
- continue
- }
- return fmt.Errorf("feature gate %q with different spec already exists: %v", name, existingSpec)
- }
- known[name] = spec
- }
- // Persist changes
- f.known.Store(known)
- return nil
- }
- // String returns a string containing all enabled feature gates, formatted as "key1=value1,key2=value2,..."
- func (f *featureGate) String() string {
- pairs := []string{}
- for k, v := range f.enabled.Load().(map[Feature]bool) {
- pairs = append(pairs, fmt.Sprintf("%s=%t", k, v))
- }
- sort.Strings(pairs)
- return strings.Join(pairs, ",")
- }
- // Enabled returns true if the key is enabled
- func (f *featureGate) Enabled(key Feature) bool {
- if v, ok := f.enabled.Load().(map[Feature]bool)[key]; ok {
- return v
- }
- if v, ok := f.known.Load().(map[Feature]FeatureSpec)[key]; ok {
- return v.Default
- }
- return false
- }
- // KnownFeatures returns a slice of strings describing the FeatureGate's known features
- // GA features are hidden from the list
- func (f *featureGate) KnownFeatures() []string {
- knownFeatures := f.known.Load().(map[Feature]FeatureSpec)
- known := make([]string, 0, len(knownFeatures))
- for k, v := range knownFeatures {
- if v.Stage == GA {
- continue
- }
- known = append(known, fmt.Sprintf("%s=true|false (%s - default=%t)", k, v.Stage, v.Default))
- }
- sort.Strings(known)
- return known
- }
- // Default feature gates instance
- var DefaultFeatureGates = NewFeatureGate()
- // Enabled checks if a feature is enabled in the default feature gates
- func Enabled(name Feature) bool {
- return DefaultFeatureGates.Enabled(name)
- }
- // SetFromMap sets feature gate values from a map in the default feature gates
- func SetFromMap(featureMap map[string]bool) error {
- return DefaultFeatureGates.SetFromMap(featureMap)
- }
|