Skip to content

Commit

Permalink
Merge pull request #673 from ykulazhenkov/pr-feature-gate
Browse files Browse the repository at this point in the history
Add featuregate package and use it in controllers
  • Loading branch information
zeeke authored Apr 4, 2024
2 parents 8f34c8b + 3b4bc74 commit c03c62f
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 12 deletions.
4 changes: 3 additions & 1 deletion controllers/sriovnetworknodepolicy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import (

sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/featuregate"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
Expand All @@ -58,7 +59,8 @@ const nodePolicySyncEventName = "node-policy-sync-event"
// SriovNetworkNodePolicyReconciler reconciles a SriovNetworkNodePolicy object
type SriovNetworkNodePolicyReconciler struct {
client.Client
Scheme *runtime.Scheme
Scheme *runtime.Scheme
FeatureGate featuregate.FeatureGate
}

//+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovnetworknodepolicies,verbs=get;list;watch;create;update;patch;delete
Expand Down
5 changes: 4 additions & 1 deletion controllers/sriovnetworknodepolicy_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
v1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/featuregate"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
)

Expand Down Expand Up @@ -211,7 +212,9 @@ func TestRenderDevicePluginConfigData(t *testing.T) {
},
}

reconciler := SriovNetworkNodePolicyReconciler{}
reconciler := SriovNetworkNodePolicyReconciler{
FeatureGate: featuregate.New(),
}

node := corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node1"}}
nodeState := sriovnetworkv1.SriovNetworkNodeState{ObjectMeta: metav1.ObjectMeta{Name: node.Name, Namespace: vars.Namespace}}
Expand Down
15 changes: 7 additions & 8 deletions controllers/sriovoperatorconfig_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
apply "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/apply"
consts "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/featuregate"
snolog "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/log"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms"
render "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render"
Expand All @@ -53,6 +54,7 @@ type SriovOperatorConfigReconciler struct {
client.Client
Scheme *runtime.Scheme
PlatformHelper platforms.Interface
FeatureGate featuregate.FeatureGate
}

//+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovoperatorconfigs,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -87,6 +89,9 @@ func (r *SriovOperatorConfigReconciler) Reconcile(ctx context.Context, req ctrl.

snolog.SetLogLevel(defaultConfig.Spec.LogLevel)

r.FeatureGate.Init(defaultConfig.Spec.FeatureGates)
logger.Info("enabled featureGates", "featureGates", r.FeatureGate.String())

if !defaultConfig.Spec.EnableInjector {
logger.Info("SR-IOV Network Resource Injector is disabled.")
}
Expand Down Expand Up @@ -172,10 +177,7 @@ func (r *SriovOperatorConfigReconciler) syncConfigDaemonSet(ctx context.Context,
} else {
data.Data["UsedSystemdMode"] = false
}
data.Data["ParallelNicConfig"] = false
if parallelConfig, ok := dc.Spec.FeatureGates[consts.ParallelNicConfigFeatureGate]; ok {
data.Data["ParallelNicConfig"] = parallelConfig
}
data.Data["ParallelNicConfig"] = r.FeatureGate.IsEnabled(consts.ParallelNicConfigFeatureGate)

envCniBinPath := os.Getenv("SRIOV_CNI_BIN_PATH")
if envCniBinPath == "" {
Expand Down Expand Up @@ -249,10 +251,7 @@ func (r *SriovOperatorConfigReconciler) syncWebhookObjs(ctx context.Context, dc
}

// check for ResourceInjectorMatchConditionFeatureGate feature gate
data.Data[consts.ResourceInjectorMatchConditionFeatureGate] = false
if resourceInjector, ok := dc.Spec.FeatureGates[consts.ResourceInjectorMatchConditionFeatureGate]; ok {
data.Data[consts.ResourceInjectorMatchConditionFeatureGate] = resourceInjector
}
data.Data[consts.ResourceInjectorMatchConditionFeatureGate] = r.FeatureGate.IsEnabled(consts.ResourceInjectorMatchConditionFeatureGate)

objs, err := render.RenderDir(path, &data)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions controllers/sriovoperatorconfig_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/featuregate"
mock_platforms "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms/mock"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms/openshift"
util "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util"
Expand Down Expand Up @@ -75,6 +76,7 @@ var _ = Describe("SriovOperatorConfig controller", Ordered, func() {
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
PlatformHelper: platformHelper,
FeatureGate: featuregate.New(),
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())

Expand Down
9 changes: 7 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (

sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
"github.com/k8snetworkplumbingwg/sriov-network-operator/controllers"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/featuregate"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/leaderelection"

snolog "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/log"
Expand Down Expand Up @@ -156,6 +157,8 @@ func main() {
os.Exit(1)
}

featureGate := featuregate.New()

if err = (&controllers.SriovNetworkReconciler{
Client: mgrGlobal.GetClient(),
Scheme: mgrGlobal.GetScheme(),
Expand All @@ -171,8 +174,9 @@ func main() {
os.Exit(1)
}
if err = (&controllers.SriovNetworkNodePolicyReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
FeatureGate: featureGate,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "SriovNetworkNodePolicy")
os.Exit(1)
Expand All @@ -181,6 +185,7 @@ func main() {
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
PlatformHelper: platformsHelper,
FeatureGate: featureGate,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "SriovOperatorConfig")
os.Exit(1)
Expand Down
64 changes: 64 additions & 0 deletions pkg/featuregate/featuregate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package featuregate

import (
"fmt"
"strings"
"sync"
)

// FeatureGate provides methods to check state of the feature
type FeatureGate interface {
// IsEnabled returns state of the feature,
// if feature name is unknown will always return false
IsEnabled(feature string) bool
// Init set state for the features from the provided map.
// completely removes the previous state
Init(features map[string]bool)
// String returns string representation of the feature state
String() string
}

// New returns default implementation of the FeatureGate interface
func New() FeatureGate {
return &featureGate{
lock: &sync.RWMutex{},
state: map[string]bool{},
}
}

type featureGate struct {
lock *sync.RWMutex
state map[string]bool
}

// IsEnabled returns state of the feature,
// if feature name is unknown will always return false
func (fg *featureGate) IsEnabled(feature string) bool {
fg.lock.RLock()
defer fg.lock.RUnlock()
return fg.state[feature]
}

// Init set state for the features from the provided map.
// completely removes the previous state
func (fg *featureGate) Init(features map[string]bool) {
fg.lock.Lock()
defer fg.lock.Unlock()
fg.state = make(map[string]bool, len(features))
for k, v := range features {
fg.state[k] = v
}
}

// String returns string representation of the feature state
func (fg *featureGate) String() string {
fg.lock.RLock()
defer fg.lock.RUnlock()
var result strings.Builder
var sep string
for k, v := range fg.state {
result.WriteString(fmt.Sprintf("%s%s:%t", sep, k, v))
sep = ", "
}
return result.String()
}
32 changes: 32 additions & 0 deletions pkg/featuregate/featuregate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package featuregate

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("FeatureGate", func() {
Context("IsEnabled", func() {
It("return false for unknown feature", func() {
Expect(New().IsEnabled("something")).To(BeFalse())
})
})
Context("Init", func() {
It("should update the state", func() {
f := New()
f.Init(map[string]bool{"feat1": true, "feat2": false})
Expect(f.IsEnabled("feat1")).To(BeTrue())
Expect(f.IsEnabled("feat2")).To(BeFalse())
})
})
Context("String", func() {
It("no features", func() {
Expect(New().String()).To(Equal(""))
})
It("print feature state", func() {
f := New()
f.Init(map[string]bool{"feat1": true, "feat2": false})
Expect(f.String()).To(And(ContainSubstring("feat1:true"), ContainSubstring("feat2:false")))
})
})
})
21 changes: 21 additions & 0 deletions pkg/featuregate/suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package featuregate

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"go.uber.org/zap/zapcore"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
)

func TestFeatureGate(t *testing.T) {
log.SetLogger(zap.New(
zap.WriteTo(GinkgoWriter),
zap.Level(zapcore.Level(-2)),
zap.UseDevMode(true)))
RegisterFailHandler(Fail)
RunSpecs(t, "Package featuregate Suite")
}

0 comments on commit c03c62f

Please sign in to comment.