-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
create operatorConfig controller for config-daemon
Signed-off-by: Sebastian Sch <[email protected]>
- Loading branch information
Showing
2 changed files
with
231 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,61 @@ | ||
package daemon | ||
|
||
import ( | ||
"context" | ||
"reflect" | ||
|
||
"k8s.io/apimachinery/pkg/api/errors" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
"sigs.k8s.io/controller-runtime/pkg/log" | ||
|
||
sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" | ||
snolog "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/log" | ||
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" | ||
) | ||
|
||
type OperatorConfigReconcile struct { | ||
client client.Client | ||
latestFeatureGates map[string]bool | ||
} | ||
|
||
func NewOperatorConfigReconcile(client client.Client) *OperatorConfigReconcile { | ||
return &OperatorConfigReconcile{client: client, latestFeatureGates: make(map[string]bool)} | ||
} | ||
|
||
func (oc *OperatorConfigReconcile) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { | ||
reqLogger := log.FromContext(ctx).WithName("Reconcile") | ||
operatorConfig := &sriovnetworkv1.SriovOperatorConfig{} | ||
err := oc.client.Get(ctx, client.ObjectKey{Namespace: req.Namespace, Name: req.Name}, operatorConfig) | ||
if err != nil { | ||
if errors.IsNotFound(err) { | ||
reqLogger.Info("OperatorConfig doesn't exist", "name", req.Name, "namespace", req.Namespace) | ||
return ctrl.Result{}, nil | ||
} | ||
reqLogger.Error(err, "Failed to operatorConfig", "name", req.Name, "namespace", req.Namespace) | ||
return ctrl.Result{}, err | ||
} | ||
|
||
// update log level | ||
snolog.SetLogLevel(operatorConfig.Spec.LogLevel) | ||
|
||
newDisableDrain := operatorConfig.Spec.DisableDrain | ||
if vars.DisableDrain != newDisableDrain { | ||
vars.DisableDrain = newDisableDrain | ||
log.Log.Info("Set Disable Drain", "value", vars.DisableDrain) | ||
} | ||
|
||
if !reflect.DeepEqual(oc.latestFeatureGates, operatorConfig.Spec.FeatureGates) { | ||
vars.FeatureGate.Init(operatorConfig.Spec.FeatureGates) | ||
oc.latestFeatureGates = operatorConfig.Spec.FeatureGates | ||
log.Log.Info("Updated featureGates", "featureGates", vars.FeatureGate.String()) | ||
} | ||
|
||
return ctrl.Result{}, nil | ||
} | ||
|
||
func (oc *OperatorConfigReconcile) SetupWithManager(mgr ctrl.Manager) error { | ||
return ctrl.NewControllerManagedBy(mgr). | ||
For(&sriovnetworkv1.SriovOperatorConfig{}). | ||
Complete(oc) | ||
} |
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,170 @@ | ||
package daemon_test | ||
|
||
import ( | ||
"context" | ||
"sync" | ||
|
||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
|
||
corev1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/client-go/kubernetes/scheme" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" | ||
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" | ||
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/daemon" | ||
snolog "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/log" | ||
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" | ||
) | ||
|
||
var _ = Describe("Daemon OperatorConfig Controller", Ordered, func() { | ||
var cancel context.CancelFunc | ||
var ctx context.Context | ||
|
||
BeforeAll(func() { | ||
By("Setup controller manager") | ||
k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ | ||
Scheme: scheme.Scheme, | ||
}) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
configController := daemon.NewOperatorConfigReconcile(k8sClient) | ||
err = configController.SetupWithManager(k8sManager) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
ctx, cancel = context.WithCancel(context.Background()) | ||
|
||
wg := sync.WaitGroup{} | ||
wg.Add(1) | ||
go func() { | ||
defer wg.Done() | ||
defer GinkgoRecover() | ||
By("Start controller manager") | ||
err := k8sManager.Start(ctx) | ||
Expect(err).ToNot(HaveOccurred()) | ||
}() | ||
|
||
DeferCleanup(func() { | ||
By("Shutdown controller manager") | ||
cancel() | ||
wg.Wait() | ||
}) | ||
|
||
err = k8sClient.Create(ctx, &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "default"}}) | ||
Expect(err).ToNot(HaveOccurred()) | ||
}) | ||
|
||
BeforeEach(func() { | ||
Expect(k8sClient.DeleteAllOf(context.Background(), &sriovnetworkv1.SriovOperatorConfig{}, client.InNamespace(testNamespace))).ToNot(HaveOccurred()) | ||
}) | ||
|
||
Context("LogLevel", func() { | ||
It("should configure the log level base on sriovOperatorConfig", func() { | ||
soc := &sriovnetworkv1.SriovOperatorConfig{ObjectMeta: metav1.ObjectMeta{ | ||
Name: consts.DefaultConfigName, | ||
Namespace: testNamespace, | ||
}, | ||
Spec: sriovnetworkv1.SriovOperatorConfigSpec{ | ||
LogLevel: 1, | ||
}, | ||
} | ||
|
||
err := k8sClient.Create(ctx, soc) | ||
Expect(err).ToNot(HaveOccurred()) | ||
validateExpectedLogLevel(1) | ||
|
||
}) | ||
|
||
It("should update the log level in runtime", func() { | ||
soc := &sriovnetworkv1.SriovOperatorConfig{ObjectMeta: metav1.ObjectMeta{ | ||
Name: consts.DefaultConfigName, | ||
Namespace: testNamespace, | ||
}, | ||
Spec: sriovnetworkv1.SriovOperatorConfigSpec{ | ||
LogLevel: 1, | ||
}, | ||
} | ||
|
||
err := k8sClient.Create(ctx, soc) | ||
Expect(err).ToNot(HaveOccurred()) | ||
validateExpectedLogLevel(1) | ||
|
||
soc.Spec.LogLevel = 2 | ||
err = k8sClient.Update(ctx, soc) | ||
Expect(err).ToNot(HaveOccurred()) | ||
validateExpectedLogLevel(2) | ||
}) | ||
}) | ||
|
||
Context("Disable Drain", func() { | ||
It("should update the skip drain flag", func() { | ||
soc := &sriovnetworkv1.SriovOperatorConfig{ObjectMeta: metav1.ObjectMeta{ | ||
Name: consts.DefaultConfigName, | ||
Namespace: testNamespace, | ||
}, | ||
Spec: sriovnetworkv1.SriovOperatorConfigSpec{ | ||
DisableDrain: true, | ||
}, | ||
} | ||
|
||
err := k8sClient.Create(ctx, soc) | ||
Expect(err).ToNot(HaveOccurred()) | ||
validateExpectedDrain(true) | ||
|
||
soc.Spec.DisableDrain = false | ||
err = k8sClient.Update(ctx, soc) | ||
Expect(err).ToNot(HaveOccurred()) | ||
validateExpectedDrain(false) | ||
}) | ||
}) | ||
|
||
Context("Feature gates", func() { | ||
It("should update the feature gates struct", func() { | ||
soc := &sriovnetworkv1.SriovOperatorConfig{ObjectMeta: metav1.ObjectMeta{ | ||
Name: consts.DefaultConfigName, | ||
Namespace: testNamespace, | ||
}, | ||
Spec: sriovnetworkv1.SriovOperatorConfigSpec{ | ||
FeatureGates: map[string]bool{ | ||
"test": true, | ||
"bla": true, | ||
}, | ||
}, | ||
} | ||
|
||
err := k8sClient.Create(ctx, soc) | ||
Expect(err).ToNot(HaveOccurred()) | ||
EventuallyWithOffset(1, func(g Gomega) { | ||
g.Expect(vars.FeatureGate.IsEnabled("test")).To(BeTrue()) | ||
}, "15s", "3s").Should(Succeed()) | ||
EventuallyWithOffset(1, func(g Gomega) { | ||
g.Expect(vars.FeatureGate.IsEnabled("bla")).To(BeTrue()) | ||
}, "15s", "3s").Should(Succeed()) | ||
|
||
soc.Spec.FeatureGates["test"] = false | ||
err = k8sClient.Update(ctx, soc) | ||
Expect(err).ToNot(HaveOccurred()) | ||
EventuallyWithOffset(1, func(g Gomega) { | ||
g.Expect(vars.FeatureGate.IsEnabled("test")).To(BeFalse()) | ||
}, "15s", "3s").Should(Succeed()) | ||
EventuallyWithOffset(1, func(g Gomega) { | ||
g.Expect(vars.FeatureGate.IsEnabled("bla")).To(BeTrue()) | ||
}, "15s", "3s").Should(Succeed()) | ||
}) | ||
}) | ||
}) | ||
|
||
func validateExpectedLogLevel(level int) { | ||
EventuallyWithOffset(1, func(g Gomega) { | ||
g.Expect(snolog.GetLogLevel()).To(Equal(level)) | ||
}, "15s", "3s").Should(Succeed()) | ||
} | ||
|
||
func validateExpectedDrain(disableDrain bool) { | ||
EventuallyWithOffset(1, func(g Gomega) { | ||
g.Expect(vars.DisableDrain).To(Equal(disableDrain)) | ||
}, "15s", "3s").Should(Succeed()) | ||
} |