Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/webhook/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,11 +413,11 @@ func TestResourceClaimValidatingWebhook(t *testing.T) {

for name, test := range tests {
t.Run(name, func(t *testing.T) {
err := featuregates.FeatureGates.SetEmulationVersion(utilversion.MajorMinor(999, 999))
err := featuregates.FeatureGates().SetEmulationVersion(utilversion.MajorMinor(999, 999))
require.NoError(t, err)

if test.featureGates != nil {
err := featuregates.FeatureGates.SetFromMap(test.featureGates)
err := featuregates.FeatureGates().SetFromMap(test.featureGates)
require.NoError(t, err)
}

Expand Down
31 changes: 20 additions & 11 deletions pkg/featuregates/featuregates.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package featuregates

import (
"strings"
"sync"

utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/version"
Expand All @@ -38,10 +39,6 @@ const (
IMEXDaemonsWithDNSNames featuregate.Feature = "IMEXDaemonsWithDNSNames"
)

// FeatureGates is a singleton representing the set of all feature gates and their values.
// It contains both project-specific feature gates and standard Kubernetes logging feature gates.
var FeatureGates featuregate.MutableVersionedFeatureGate

// defaultFeatureGates contains the default settings for all project-specific feature gates.
// These will be registered with the standard Kubernetes feature gate system.
var defaultFeatureGates = map[featuregate.Feature]featuregate.VersionedSpecs{
Expand All @@ -68,9 +65,21 @@ var defaultFeatureGates = map[featuregate.Feature]featuregate.VersionedSpecs{
},
}

// init instantiates and sets the singleton 'FeatureGates' variable with newFeatureGates().
func init() {
FeatureGates = newFeatureGates(parseProjectVersion())
var (
featureGatesOnce sync.Once
featureGates featuregate.MutableVersionedFeatureGate
)

// FeatureGates instantiates and returns the package-level singleton representing
// the set of all feature gates and their values.
// It contains both project-specific feature gates and standard Kubernetes logging feature gates.
func FeatureGates() featuregate.MutableVersionedFeatureGate {
if featureGates == nil {
featureGatesOnce.Do(func() {
featureGates = newFeatureGates(parseProjectVersion())
})
}
return featureGates
}

// parseProjectVersion parses the project version string and returns major.minor version.
Expand Down Expand Up @@ -106,21 +115,21 @@ func newFeatureGates(version *version.Version) featuregate.MutableVersionedFeatu
// Enabled returns true if the specified feature gate is enabled in the global FeatureGates singleton.
// This is a convenience function that uses the global feature gate registry.
func Enabled(feature featuregate.Feature) bool {
return FeatureGates.Enabled(feature)
return FeatureGates().Enabled(feature)
}

// KnownFeatures returns a list of known feature gates with their descriptions.
func KnownFeatures() []string {
return FeatureGates.KnownFeatures()
return FeatureGates().KnownFeatures()
}

// ToMap returns all known feature gates as a map[string]bool suitable for
// template rendering (e.g., {"FeatureA": true, "FeatureB": false}).
// Returns an empty map if no feature gates are configured.
func ToMap() map[string]bool {
result := make(map[string]bool)
for feature := range FeatureGates.GetAll() {
result[string(feature)] = FeatureGates.Enabled(feature)
for feature := range FeatureGates().GetAll() {
result[string(feature)] = FeatureGates().Enabled(feature)
}
return result
}
2 changes: 1 addition & 1 deletion pkg/flags/featuregates.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (f *FeatureGateConfig) Flags() []cli.Flag {
Name: "feature-gates",
Usage: "A set of key=value pairs that describe feature gates for alpha/experimental features. " +
"Options are:\n " + strings.Join(featuregates.KnownFeatures(), "\n "),
Value: featuregates.FeatureGates.(pflag.Value), //nolint:forcetypeassert // No need for type check: FeatureGates is a *featuregate.featureGate, which implements pflag.Value.
Value: featuregates.FeatureGates().(pflag.Value), //nolint:forcetypeassert // No need for type check: FeatureGates is a *featuregate.featureGate, which implements pflag.Value.
})

var flags []cli.Flag
Expand Down
10 changes: 5 additions & 5 deletions pkg/flags/featuregates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ var (

func TestFeatureGateConfigBasicFunctionality(t *testing.T) {
// Test that the config provides basic feature gate functionality
fg := featuregates.FeatureGates
fg := featuregates.FeatureGates()

// Test that the feature gate is functional
require.NotNil(t, fg, "FeatureGate should not be nil")
Expand Down Expand Up @@ -93,7 +93,7 @@ func TestFeatureGateConfigSeparationOfConcerns(t *testing.T) {
func TestFeatureGateConfigSetFromMap(t *testing.T) {
t.Run("EnableDisabledFeature", func(t *testing.T) {
// Test that we can enable a disabled feature
fg := featuregates.FeatureGates
fg := featuregates.FeatureGates()

// Test enabling a disabled feature - we know AllAlpha starts as false
require.False(t, featuregates.Enabled(testValidDisabledFeature), "AllAlpha feature should start as false")
Expand All @@ -116,7 +116,7 @@ func TestFeatureGateConfigSetFromMap(t *testing.T) {

t.Run("DisableEnabledFeature", func(t *testing.T) {
// Test that we can disable an enabled feature
fg := featuregates.FeatureGates
fg := featuregates.FeatureGates()

// First enable AllBeta so we can test disabling it
err := fg.SetFromMap(map[string]bool{
Expand Down Expand Up @@ -145,7 +145,7 @@ func TestFeatureGateConfigSetFromMap(t *testing.T) {
}

func TestFeatureGateConfigKnownFeatures(t *testing.T) {
fg := featuregates.FeatureGates
fg := featuregates.FeatureGates()

knownFeatures := fg.KnownFeatures()
require.NotEmpty(t, knownFeatures, "Should have some known features")
Expand All @@ -163,7 +163,7 @@ func TestFeatureGateConfigKnownFeatures(t *testing.T) {
}

func TestFeatureGateConfigErrorHandling(t *testing.T) {
fg := featuregates.FeatureGates
fg := featuregates.FeatureGates()

// Test error handling with invalid features
err := fg.SetFromMap(map[string]bool{
Expand Down
2 changes: 1 addition & 1 deletion pkg/flags/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func NewLoggingConfig() *LoggingConfig {
// line flags and before running any code which emits log entries.
// It uses the global feature gate singleton.
func (l *LoggingConfig) Apply() error {
return logsapi.ValidateAndApply(l.config, featuregates.FeatureGates)
return logsapi.ValidateAndApply(l.config, featuregates.FeatureGates())
}

// Flags returns the flags for logging configuration (NOT including feature gates).
Expand Down