-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
233 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package feature | ||
|
||
import "sync/atomic" | ||
|
||
// Stage is feature state. | ||
type Stage int | ||
|
||
const ( | ||
StageAlpha Stage = iota | ||
StageBeta | ||
StageStable | ||
StageDeprecated | ||
) | ||
|
||
// Feature is the feature interface. | ||
type Feature struct { | ||
name string | ||
enabled atomic.Bool | ||
description string | ||
fromVersion string | ||
toVersion string | ||
stage Stage | ||
} | ||
|
||
// Name returns feature name. | ||
func (f *Feature) Name() string { | ||
return f.name | ||
} | ||
|
||
// Enabled returns true if the feature enbaled. | ||
func (f *Feature) Enabled() bool { | ||
return f.enabled.Load() | ||
} | ||
|
||
// Description returns the description for the feature. | ||
func (f *Feature) Description() string { | ||
return f.description | ||
} | ||
|
||
// FromVersion The "From" column contains the Feature release when a feature is introduced or its release stage is changed. | ||
func (f *Feature) FromVersion() string { | ||
return f.fromVersion | ||
} | ||
|
||
// ToVersion if not empty, contains the last Feature release in which you can still use a feature gate. | ||
// If the feature stage is either "Deprecated" or "GA", the "To" column is the Feature release when the feature is removed. | ||
func (f *Feature) ToVersion() string { | ||
return f.toVersion | ||
} | ||
|
||
// Stage returns the feature state. | ||
func (f *Feature) Stage() Stage { | ||
return f.stage | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package feature | ||
|
||
import "flag" | ||
|
||
var globalRegistry = NewRegistry() | ||
|
||
func init() { | ||
flag.CommandLine.Var(globalRegistry, "feature-gates", "A set of key=value pairs that describe feature gates for alpha/beta/stable features.") | ||
} | ||
|
||
// Register register a feature gate. | ||
func Register(name string, enabled bool, opts ...Option) (*Feature, error) { | ||
return globalRegistry.Register(name, enabled, opts...) | ||
} | ||
|
||
// MustRegister must register a feature gate. | ||
func MustRegister(name string, enabled bool, opts ...Option) *Feature { | ||
return globalRegistry.MustRegister(name, enabled, opts...) | ||
} | ||
|
||
// Set parses the feature flags: foo=true,bar=false. | ||
func Set(featureFlags string) error { | ||
return globalRegistry.Set(featureFlags) | ||
} | ||
|
||
// SetEnabled set feature enabled. | ||
func SetEnabled(name string, enabled bool) error { | ||
return globalRegistry.SetEnabled(name, enabled) | ||
} | ||
|
||
// Visit visits all the features. | ||
func Visit(f func(*Feature)) { | ||
globalRegistry.Visit(f) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package feature_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/go-kratos/feature" | ||
) | ||
|
||
var ( | ||
foo = feature.MustRegister("foo", true) | ||
bar = feature.MustRegister("bar", false, | ||
feature.WithFeautreStage(feature.StageAlpha), | ||
feature.WithFeautreFromVersion("0.0.1"), | ||
feature.WithFeautreToVersion("1.0.0"), | ||
feature.WithFeautreDescription("A foo feature"), | ||
) | ||
) | ||
|
||
func TestFeatureVisit(t *testing.T) { | ||
feature.Visit(func(f *feature.Feature) { | ||
t.Logf("feature: %s %t", f.Name(), f.Enabled()) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module github.com/go-kratos/feature | ||
|
||
go 1.19 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package feature | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
// Option is feature option. | ||
type Option func(*Feature) | ||
|
||
// WithFeautreStage with the feature stage. | ||
func WithFeautreStage(stage Stage) Option { | ||
return func(f *Feature) { | ||
f.stage = stage | ||
} | ||
} | ||
|
||
// WithFeautreDescription with the feautre description. | ||
func WithFeautreDescription(description string) Option { | ||
return func(f *Feature) { | ||
f.description = description | ||
} | ||
} | ||
|
||
// WithFeautreToVersion with the feautre to version. | ||
func WithFeautreToVersion(version string) Option { | ||
return func(f *Feature) { | ||
f.toVersion = version | ||
} | ||
} | ||
|
||
// WithFeautreFromVersion with the feautre to version. | ||
func WithFeautreFromVersion(version string) Option { | ||
return func(f *Feature) { | ||
f.toVersion = version | ||
} | ||
} | ||
|
||
// Registry is feature gates registry. | ||
type Registry struct { | ||
features map[string]*Feature | ||
} | ||
|
||
// NewRegistry new a feature registry. | ||
func NewRegistry() *Registry { | ||
return &Registry{ | ||
features: make(map[string]*Feature), | ||
} | ||
} | ||
|
||
// Register register a feature gate. | ||
func (r *Registry) Register(name string, enabled bool, opts ...Option) (*Feature, error) { | ||
if _, ok := r.features[name]; ok { | ||
return nil, fmt.Errorf("feature gate %s is registered", name) | ||
} | ||
feature := &Feature{ | ||
name: name, | ||
} | ||
feature.enabled.Store(enabled) | ||
for _, o := range opts { | ||
o(feature) | ||
} | ||
r.features[feature.Name()] = feature | ||
return feature, nil | ||
} | ||
|
||
// MustRegister must register a feature gate. | ||
func (r *Registry) MustRegister(name string, enabled bool, opts ...Option) *Feature { | ||
feature, err := r.Register(name, enabled, opts...) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return feature | ||
} | ||
|
||
// SetEnabled set feature enabled. | ||
func (r *Registry) SetEnabled(name string, enabled bool) error { | ||
f, ok := r.features[name] | ||
if !ok { | ||
return fmt.Errorf("not found feature name: %s", name) | ||
} | ||
f.enabled.Store(enabled) | ||
return nil | ||
} | ||
|
||
// Set parses the feature flags: foo=true,bar=false. | ||
func (r *Registry) Set(featureFlags string) error { | ||
fs := strings.Split(featureFlags, ",") | ||
for _, s := range fs { | ||
feature := strings.Split(s, "=") | ||
name := feature[0] | ||
enabled, err := strconv.ParseBool(feature[1]) | ||
if err != nil { | ||
return err | ||
} | ||
if err := r.SetEnabled(name, enabled); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (r *Registry) String() string { | ||
pairs := []string{} | ||
for name, feature := range r.features { | ||
enabled := feature.enabled.Load() | ||
pairs = append(pairs, fmt.Sprintf("%s=%t", name, enabled)) | ||
} | ||
return strings.Join(pairs, ",") | ||
|
||
} | ||
|
||
// Visit visits all the features. | ||
func (r *Registry) Visit(f func(*Feature)) { | ||
for _, feature := range r.features { | ||
f(feature) | ||
} | ||
} |