Skip to content

Commit a5e3a74

Browse files
committed
create operatorConfig controller for config-daemon
Signed-off-by: Sebastian Sch <[email protected]>
1 parent 92b3e34 commit a5e3a74

File tree

2 files changed

+231
-0
lines changed

2 files changed

+231
-0
lines changed

pkg/daemon/config.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package daemon
2+
3+
import (
4+
"context"
5+
"reflect"
6+
7+
"k8s.io/apimachinery/pkg/api/errors"
8+
ctrl "sigs.k8s.io/controller-runtime"
9+
"sigs.k8s.io/controller-runtime/pkg/client"
10+
"sigs.k8s.io/controller-runtime/pkg/log"
11+
12+
sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
13+
snolog "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/log"
14+
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
15+
)
16+
17+
type OperatorConfigReconcile struct {
18+
client client.Client
19+
latestFeatureGates map[string]bool
20+
}
21+
22+
func NewOperatorConfigReconcile(client client.Client) *OperatorConfigReconcile {
23+
return &OperatorConfigReconcile{client: client, latestFeatureGates: make(map[string]bool)}
24+
}
25+
26+
func (oc *OperatorConfigReconcile) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
27+
reqLogger := log.FromContext(ctx).WithName("Reconcile")
28+
operatorConfig := &sriovnetworkv1.SriovOperatorConfig{}
29+
err := oc.client.Get(ctx, client.ObjectKey{Namespace: req.Namespace, Name: req.Name}, operatorConfig)
30+
if err != nil {
31+
if errors.IsNotFound(err) {
32+
reqLogger.Info("OperatorConfig doesn't exist", "name", req.Name, "namespace", req.Namespace)
33+
return ctrl.Result{}, nil
34+
}
35+
reqLogger.Error(err, "Failed to operatorConfig", "name", req.Name, "namespace", req.Namespace)
36+
return ctrl.Result{}, err
37+
}
38+
39+
// update log level
40+
snolog.SetLogLevel(operatorConfig.Spec.LogLevel)
41+
42+
newDisableDrain := operatorConfig.Spec.DisableDrain
43+
if vars.DisableDrain != newDisableDrain {
44+
vars.DisableDrain = newDisableDrain
45+
log.Log.Info("Set Disable Drain", "value", vars.DisableDrain)
46+
}
47+
48+
if !reflect.DeepEqual(oc.latestFeatureGates, operatorConfig.Spec.FeatureGates) {
49+
vars.FeatureGate.Init(operatorConfig.Spec.FeatureGates)
50+
oc.latestFeatureGates = operatorConfig.Spec.FeatureGates
51+
log.Log.Info("Updated featureGates", "featureGates", vars.FeatureGate.String())
52+
}
53+
54+
return ctrl.Result{}, nil
55+
}
56+
57+
func (oc *OperatorConfigReconcile) SetupWithManager(mgr ctrl.Manager) error {
58+
return ctrl.NewControllerManagedBy(mgr).
59+
For(&sriovnetworkv1.SriovOperatorConfig{}).
60+
Complete(oc)
61+
}

pkg/daemon/config_test.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package daemon_test
2+
3+
import (
4+
"context"
5+
"sync"
6+
7+
. "github.com/onsi/ginkgo/v2"
8+
. "github.com/onsi/gomega"
9+
10+
corev1 "k8s.io/api/core/v1"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/client-go/kubernetes/scheme"
13+
ctrl "sigs.k8s.io/controller-runtime"
14+
"sigs.k8s.io/controller-runtime/pkg/client"
15+
16+
sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
17+
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
18+
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/daemon"
19+
snolog "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/log"
20+
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
21+
)
22+
23+
var _ = Describe("Daemon OperatorConfig Controller", Ordered, func() {
24+
var cancel context.CancelFunc
25+
var ctx context.Context
26+
27+
BeforeAll(func() {
28+
By("Setup controller manager")
29+
k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
30+
Scheme: scheme.Scheme,
31+
})
32+
Expect(err).ToNot(HaveOccurred())
33+
34+
configController := daemon.NewOperatorConfigReconcile(k8sClient)
35+
err = configController.SetupWithManager(k8sManager)
36+
Expect(err).ToNot(HaveOccurred())
37+
38+
ctx, cancel = context.WithCancel(context.Background())
39+
40+
wg := sync.WaitGroup{}
41+
wg.Add(1)
42+
go func() {
43+
defer wg.Done()
44+
defer GinkgoRecover()
45+
By("Start controller manager")
46+
err := k8sManager.Start(ctx)
47+
Expect(err).ToNot(HaveOccurred())
48+
}()
49+
50+
DeferCleanup(func() {
51+
By("Shutdown controller manager")
52+
cancel()
53+
wg.Wait()
54+
})
55+
56+
err = k8sClient.Create(ctx, &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "default"}})
57+
Expect(err).ToNot(HaveOccurred())
58+
})
59+
60+
BeforeEach(func() {
61+
Expect(k8sClient.DeleteAllOf(context.Background(), &sriovnetworkv1.SriovOperatorConfig{}, client.InNamespace(testNamespace))).ToNot(HaveOccurred())
62+
})
63+
64+
Context("LogLevel", func() {
65+
It("should configure the log level base on sriovOperatorConfig", func() {
66+
soc := &sriovnetworkv1.SriovOperatorConfig{ObjectMeta: metav1.ObjectMeta{
67+
Name: consts.DefaultConfigName,
68+
Namespace: testNamespace,
69+
},
70+
Spec: sriovnetworkv1.SriovOperatorConfigSpec{
71+
LogLevel: 1,
72+
},
73+
}
74+
75+
err := k8sClient.Create(ctx, soc)
76+
Expect(err).ToNot(HaveOccurred())
77+
validateExpectedLogLevel(1)
78+
79+
})
80+
81+
It("should update the log level in runtime", func() {
82+
soc := &sriovnetworkv1.SriovOperatorConfig{ObjectMeta: metav1.ObjectMeta{
83+
Name: consts.DefaultConfigName,
84+
Namespace: testNamespace,
85+
},
86+
Spec: sriovnetworkv1.SriovOperatorConfigSpec{
87+
LogLevel: 1,
88+
},
89+
}
90+
91+
err := k8sClient.Create(ctx, soc)
92+
Expect(err).ToNot(HaveOccurred())
93+
validateExpectedLogLevel(1)
94+
95+
soc.Spec.LogLevel = 2
96+
err = k8sClient.Update(ctx, soc)
97+
Expect(err).ToNot(HaveOccurred())
98+
validateExpectedLogLevel(2)
99+
})
100+
})
101+
102+
Context("Disable Drain", func() {
103+
It("should update the skip drain flag", func() {
104+
soc := &sriovnetworkv1.SriovOperatorConfig{ObjectMeta: metav1.ObjectMeta{
105+
Name: consts.DefaultConfigName,
106+
Namespace: testNamespace,
107+
},
108+
Spec: sriovnetworkv1.SriovOperatorConfigSpec{
109+
DisableDrain: true,
110+
},
111+
}
112+
113+
err := k8sClient.Create(ctx, soc)
114+
Expect(err).ToNot(HaveOccurred())
115+
validateExpectedDrain(true)
116+
117+
soc.Spec.DisableDrain = false
118+
err = k8sClient.Update(ctx, soc)
119+
Expect(err).ToNot(HaveOccurred())
120+
validateExpectedDrain(false)
121+
})
122+
})
123+
124+
Context("Feature gates", func() {
125+
It("should update the feature gates struct", func() {
126+
soc := &sriovnetworkv1.SriovOperatorConfig{ObjectMeta: metav1.ObjectMeta{
127+
Name: consts.DefaultConfigName,
128+
Namespace: testNamespace,
129+
},
130+
Spec: sriovnetworkv1.SriovOperatorConfigSpec{
131+
FeatureGates: map[string]bool{
132+
"test": true,
133+
"bla": true,
134+
},
135+
},
136+
}
137+
138+
err := k8sClient.Create(ctx, soc)
139+
Expect(err).ToNot(HaveOccurred())
140+
EventuallyWithOffset(1, func(g Gomega) {
141+
g.Expect(vars.FeatureGate.IsEnabled("test")).To(BeTrue())
142+
}, "15s", "3s").Should(Succeed())
143+
EventuallyWithOffset(1, func(g Gomega) {
144+
g.Expect(vars.FeatureGate.IsEnabled("bla")).To(BeTrue())
145+
}, "15s", "3s").Should(Succeed())
146+
147+
soc.Spec.FeatureGates["test"] = false
148+
err = k8sClient.Update(ctx, soc)
149+
Expect(err).ToNot(HaveOccurred())
150+
EventuallyWithOffset(1, func(g Gomega) {
151+
g.Expect(vars.FeatureGate.IsEnabled("test")).To(BeFalse())
152+
}, "15s", "3s").Should(Succeed())
153+
EventuallyWithOffset(1, func(g Gomega) {
154+
g.Expect(vars.FeatureGate.IsEnabled("bla")).To(BeTrue())
155+
}, "15s", "3s").Should(Succeed())
156+
})
157+
})
158+
})
159+
160+
func validateExpectedLogLevel(level int) {
161+
EventuallyWithOffset(1, func(g Gomega) {
162+
g.Expect(snolog.GetLogLevel()).To(Equal(level))
163+
}, "15s", "3s").Should(Succeed())
164+
}
165+
166+
func validateExpectedDrain(disableDrain bool) {
167+
EventuallyWithOffset(1, func(g Gomega) {
168+
g.Expect(vars.DisableDrain).To(Equal(disableDrain))
169+
}, "15s", "3s").Should(Succeed())
170+
}

0 commit comments

Comments
 (0)