• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

k8snetworkplumbingwg / sriov-network-operator / 13099616970

02 Feb 2025 02:19PM UTC coverage: 47.363% (-0.05%) from 47.415%
13099616970

Pull #839

github

web-flow
Merge 9eab1daac into aaba54c33
Pull Request #839: Bump github action artifact to v4

7265 of 15339 relevant lines covered (47.36%)

0.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.68
/controllers/drain_controller.go
1
/*
2
Copyright 2021.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package controllers
18

19
import (
20
        "context"
21
        "fmt"
22
        "sync"
23

24
        corev1 "k8s.io/api/core/v1"
25
        "k8s.io/apimachinery/pkg/api/errors"
26
        "k8s.io/apimachinery/pkg/runtime"
27
        "k8s.io/apimachinery/pkg/types"
28
        "k8s.io/client-go/tools/record"
29
        "k8s.io/client-go/util/workqueue"
30
        ctrl "sigs.k8s.io/controller-runtime"
31
        "sigs.k8s.io/controller-runtime/pkg/builder"
32
        "sigs.k8s.io/controller-runtime/pkg/client"
33
        "sigs.k8s.io/controller-runtime/pkg/controller"
34
        "sigs.k8s.io/controller-runtime/pkg/event"
35
        "sigs.k8s.io/controller-runtime/pkg/handler"
36
        "sigs.k8s.io/controller-runtime/pkg/log"
37
        "sigs.k8s.io/controller-runtime/pkg/reconcile"
38

39
        sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
40
        constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
41
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/drain"
42
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms"
43
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
44
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
45
)
46

47
type DrainReconcile struct {
48
        client.Client
49
        Scheme   *runtime.Scheme
50
        recorder record.EventRecorder
51
        drainer  drain.DrainInterface
52

53
        drainCheckMutex sync.Mutex
54
}
55

56
func NewDrainReconcileController(client client.Client, Scheme *runtime.Scheme, recorder record.EventRecorder, platformHelper platforms.Interface) (*DrainReconcile, error) {
1✔
57
        drainer, err := drain.NewDrainer(platformHelper)
1✔
58
        if err != nil {
1✔
59
                return nil, err
×
60
        }
×
61

62
        return &DrainReconcile{
1✔
63
                client,
1✔
64
                Scheme,
1✔
65
                recorder,
1✔
66
                drainer,
1✔
67
                sync.Mutex{}}, nil
1✔
68
}
69

70
//+kubebuilder:rbac:groups="",resources=nodes,verbs=get;list;watch;update;patch
71
//+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovnodestates,verbs=get;list;watch
72
//+kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
73

74
// Reconcile is part of the main kubernetes reconciliation loop which aims to
75
// move the current state of the cluster closer to the desired state.
76
// For more details, check Reconcile and its Result here:
77
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile
78
func (dr *DrainReconcile) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
1✔
79
        reqLogger := log.FromContext(ctx)
1✔
80
        reqLogger.Info("Reconciling Drain")
1✔
81

1✔
82
        req.Namespace = vars.Namespace
1✔
83

1✔
84
        // get node object
1✔
85
        node := &corev1.Node{}
1✔
86
        found, err := dr.getObject(ctx, req, node)
1✔
87
        if err != nil {
1✔
88
                reqLogger.Error(err, "failed to get node object")
×
89
                return ctrl.Result{}, err
×
90
        }
×
91
        if !found {
2✔
92
                reqLogger.Info("node not found don't, requeue the request")
1✔
93
                return ctrl.Result{}, nil
1✔
94
        }
1✔
95

96
        // get sriovNodeNodeState object
97
        nodeNetworkState := &sriovnetworkv1.SriovNetworkNodeState{}
1✔
98
        found, err = dr.getObject(ctx, req, nodeNetworkState)
1✔
99
        if err != nil {
1✔
100
                reqLogger.Error(err, "failed to get sriovNetworkNodeState object")
×
101
                return ctrl.Result{}, err
×
102
        }
×
103
        if !found {
2✔
104
                reqLogger.Info("sriovNetworkNodeState not found, don't requeue the request")
1✔
105
                return ctrl.Result{}, nil
1✔
106
        }
1✔
107

108
        // create the drain state annotation if it doesn't exist in the sriovNetworkNodeState object
109
        nodeStateDrainAnnotationCurrent, nodeStateExist, err := dr.ensureAnnotationExists(ctx, nodeNetworkState, constants.NodeStateDrainAnnotationCurrent)
1✔
110
        if err != nil {
1✔
111
                reqLogger.Error(err, "failed to ensure nodeStateDrainAnnotation")
×
112
                return ctrl.Result{}, err
×
113
        }
×
114

115
        // create the drain state annotation if it doesn't exist in the node object
116
        nodeDrainAnnotation, nodeExist, err := dr.ensureAnnotationExists(ctx, node, constants.NodeDrainAnnotation)
1✔
117
        if err != nil {
1✔
118
                reqLogger.Error(err, "failed to ensure nodeStateDrainAnnotation")
×
119
                return ctrl.Result{}, err
×
120
        }
×
121

122
        // requeue the request if we needed to add any of the annotations
123
        if !nodeExist || !nodeStateExist {
2✔
124
                return ctrl.Result{Requeue: true}, nil
1✔
125
        }
1✔
126
        reqLogger.V(2).Info("Drain annotations", "nodeAnnotation", nodeDrainAnnotation, "nodeStateAnnotation", nodeStateDrainAnnotationCurrent)
1✔
127

1✔
128
        // Check the node request
1✔
129
        if nodeDrainAnnotation == constants.DrainIdle {
2✔
130
                // this cover the case the node is on idle
1✔
131

1✔
132
                // node request to be on idle and the currect state is idle
1✔
133
                // we don't do anything
1✔
134
                if nodeStateDrainAnnotationCurrent == constants.DrainIdle {
2✔
135
                        reqLogger.Info("node and nodeState are on idle nothing todo")
1✔
136
                        return reconcile.Result{}, nil
1✔
137
                }
1✔
138

139
                // we have two options here:
140
                // 1. node request idle and the current status is drain complete
141
                // this means the daemon finish is work, so we need to clean the drain
142
                //
143
                // 2. the operator is still draining the node but maybe the sriov policy changed and the daemon
144
                //  doesn't need to drain anymore, so we can stop the drain
145
                if nodeStateDrainAnnotationCurrent == constants.DrainComplete ||
1✔
146
                        nodeStateDrainAnnotationCurrent == constants.Draining {
2✔
147
                        return dr.handleNodeIdleNodeStateDrainingOrCompleted(ctx, &reqLogger, node, nodeNetworkState)
1✔
148
                }
1✔
149
        }
150

151
        // this cover the case a node request to drain or reboot
152
        if nodeDrainAnnotation == constants.DrainRequired ||
1✔
153
                nodeDrainAnnotation == constants.RebootRequired {
2✔
154
                return dr.handleNodeDrainOrReboot(ctx, &reqLogger, node, nodeNetworkState, nodeDrainAnnotation, nodeStateDrainAnnotationCurrent)
1✔
155
        }
1✔
156

157
        reqLogger.Error(nil, "unexpected node drain annotation")
×
158
        return reconcile.Result{}, fmt.Errorf("unexpected node drain annotation")
×
159
}
160

161
func (dr *DrainReconcile) getObject(ctx context.Context, req ctrl.Request, object client.Object) (bool, error) {
1✔
162
        err := dr.Get(ctx, req.NamespacedName, object)
1✔
163
        if err != nil {
2✔
164
                if errors.IsNotFound(err) {
2✔
165
                        return false, nil
1✔
166
                }
1✔
167
                return false, err
×
168
        }
169
        return true, nil
1✔
170
}
171

172
func (dr *DrainReconcile) ensureAnnotationExists(ctx context.Context, object client.Object, key string) (string, bool, error) {
1✔
173
        value, exist := object.GetAnnotations()[key]
1✔
174
        if !exist {
2✔
175
                err := utils.AnnotateObject(ctx, object, key, constants.DrainIdle, dr.Client)
1✔
176
                if err != nil {
1✔
177
                        return "", false, err
×
178
                }
×
179
                return constants.DrainIdle, false, nil
1✔
180
        }
181

182
        return value, true, nil
1✔
183
}
184

185
// SetupWithManager sets up the controller with the Manager.
186
func (dr *DrainReconcile) SetupWithManager(mgr ctrl.Manager) error {
1✔
187
        createUpdateEnqueue := handler.Funcs{
1✔
188
                CreateFunc: func(ctx context.Context, e event.CreateEvent, q workqueue.RateLimitingInterface) {
2✔
189
                        q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
1✔
190
                                Namespace: vars.Namespace,
1✔
191
                                Name:      e.Object.GetName(),
1✔
192
                        }})
1✔
193
                },
1✔
194
                UpdateFunc: func(ctx context.Context, e event.UpdateEvent, q workqueue.RateLimitingInterface) {
×
195
                        q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
×
196
                                Namespace: vars.Namespace,
×
197
                                Name:      e.ObjectNew.GetName(),
×
198
                        }})
×
199
                },
×
200
        }
201

202
        // Watch for spec and annotation changes
203
        nodePredicates := builder.WithPredicates(DrainAnnotationPredicate{})
1✔
204
        nodeStatePredicates := builder.WithPredicates(DrainStateAnnotationPredicate{})
1✔
205

1✔
206
        return ctrl.NewControllerManagedBy(mgr).
1✔
207
                WithOptions(controller.Options{MaxConcurrentReconciles: 50}).
1✔
208
                For(&corev1.Node{}, nodePredicates).
1✔
209
                Watches(&sriovnetworkv1.SriovNetworkNodeState{}, createUpdateEnqueue, nodeStatePredicates).
1✔
210
                Complete(dr)
1✔
211
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc