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

kubevirt / hyperconverged-cluster-operator / 16489733750

24 Jul 2025 06:42AM UTC coverage: 75.337% (+0.06%) from 75.274%
16489733750

Pull #3628

github

web-flow
Merge 8dc51367f into ca3401979
Pull Request #3628: passt: Deploy NAD on openshift-cnv namespace

8 of 8 new or added lines in 1 file covered. (100.0%)

22 existing lines in 4 files now uncovered.

6940 of 9212 relevant lines covered (75.34%)

1.79 hits per line

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

97.77
/controllers/operandhandler/operandHandler.go
1
package operandhandler
2

3
import (
4
        "context"
5
        "fmt"
6
        "time"
7

8
        "golang.org/x/sync/errgroup"
9
        corev1 "k8s.io/api/core/v1"
10
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11
        "k8s.io/apimachinery/pkg/runtime"
12
        "sigs.k8s.io/controller-runtime/pkg/client"
13
        logf "sigs.k8s.io/controller-runtime/pkg/log"
14

15
        kubevirtcorev1 "kubevirt.io/api/core/v1"
16
        cdiv1beta1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"
17

18
        hcov1beta1 "github.com/kubevirt/hyperconverged-cluster-operator/api/v1beta1"
19
        "github.com/kubevirt/hyperconverged-cluster-operator/controllers/common"
20
        "github.com/kubevirt/hyperconverged-cluster-operator/controllers/handlers"
21
        "github.com/kubevirt/hyperconverged-cluster-operator/controllers/handlers/passt"
22
        "github.com/kubevirt/hyperconverged-cluster-operator/controllers/operands"
23
        "github.com/kubevirt/hyperconverged-cluster-operator/pkg/monitoring/hyperconverged/metrics"
24
        hcoutil "github.com/kubevirt/hyperconverged-cluster-operator/pkg/util"
25
)
26

27
const (
28
        reconcileFailed       = "ReconcileFailed"
29
        ErrCDIUninstall       = "ErrCDIUninstall"
30
        uninstallCDIErrorMsg  = "The uninstall request failed on CDI component: "
31
        ErrVirtUninstall      = "ErrVirtUninstall"
32
        uninstallVirtErrorMsg = "The uninstall request failed on virt component: "
33
        ErrHCOUninstall       = "ErrHCOUninstall"
34
        uninstallHCOErrorMsg  = "The uninstall request failed on dependent components, please check their logs."
35
        deleteTimeOut         = 30 * time.Second
36
)
37

38
var (
39
        logger = logf.Log.WithName("operandHandlerInit")
40
)
41

42
type OperandHandler struct {
43
        client   client.Client
44
        operands []operands.Operand
45
        // save for deletions
46
        objects      []client.Object
47
        eventEmitter hcoutil.EventEmitter
48
}
49

50
func NewOperandHandler(client client.Client, scheme *runtime.Scheme, ci hcoutil.ClusterInfo, eventEmitter hcoutil.EventEmitter) *OperandHandler {
2✔
51
        operandList := []operands.Operand{
2✔
52
                handlers.NewKvPriorityClassHandler(client, scheme),
2✔
53
                handlers.NewKubevirtHandler(client, scheme),
2✔
54
                handlers.NewCdiHandler(client, scheme),
2✔
55
                handlers.NewCnaHandler(client, scheme),
2✔
56
                handlers.NewAAQHandler(client, scheme),
2✔
57
                passt.NewPasstDaemonSetHandler(client, scheme),
2✔
58
        }
2✔
59

2✔
60
        if ci.IsOpenshift() {
4✔
61
                operandList = append(operandList, []operands.Operand{
2✔
62
                        handlers.NewSspHandler(client, scheme),
2✔
63
                        handlers.NewCliDownloadHandler(client, scheme),
2✔
64
                        handlers.NewCliDownloadsRouteHandler(client, scheme),
2✔
65
                        operands.NewServiceHandler(client, scheme, handlers.NewCliDownloadsService),
2✔
66
                        passt.NewPasstServiceAccountHandler(client, scheme),
2✔
67
                        passt.NewPasstSecurityContextConstraintsHandler(client, scheme),
2✔
68
                }...)
2✔
69
        }
2✔
70

71
        if ci.IsOpenshift() && ci.IsConsolePluginImageProvided() {
4✔
72
                operandList = append(operandList, handlers.NewConsoleHandler(client))
2✔
73
                operandList = append(operandList, operands.NewServiceHandler(client, scheme, handlers.NewKvUIPluginSvc))
2✔
74
                operandList = append(operandList, operands.NewServiceHandler(client, scheme, handlers.NewKvUIProxySvc))
2✔
75
        }
2✔
76

77
        if ci.IsManagedByOLM() {
4✔
78
                operandList = append(operandList, handlers.NewCsvHandler(client, ci))
2✔
79
        }
2✔
80

81
        return &OperandHandler{
2✔
82
                client:       client,
2✔
83
                operands:     operandList,
2✔
84
                eventEmitter: eventEmitter,
2✔
85
        }
2✔
86
}
87

88
// FirstUseInitiation is a lazy init function
89
// The k8s client is not available when calling to NewOperandHandler.
90
// Initial operations that need to read/write from the cluster can only be done when the client is already working.
91
func (h *OperandHandler) FirstUseInitiation(scheme *runtime.Scheme, ci hcoutil.ClusterInfo, hc *hcov1beta1.HyperConverged) {
2✔
92
        h.objects = make([]client.Object, 0)
2✔
93

2✔
94
        h.addOperand(scheme, hc, passt.NewPasstNetworkAttachmentDefinitionHandler)
2✔
95

2✔
96
        if ci.IsOpenshift() {
4✔
97
                h.addOperands(scheme, hc, handlers.GetQuickStartHandlers)
2✔
98
                h.addOperands(scheme, hc, handlers.GetDashboardHandlers)
2✔
99
                h.addOperands(scheme, hc, handlers.GetImageStreamHandlers)
2✔
100
                h.addOperand(scheme, hc, handlers.NewVirtioWinCmHandler)
2✔
101
                h.addOperand(scheme, hc, handlers.NewVirtioWinCmReaderRoleHandler)
2✔
102
                h.addOperand(scheme, hc, handlers.NewVirtioWinCmReaderRoleBindingHandler)
2✔
103
        }
2✔
104

105
        if ci.IsOpenshift() && ci.IsConsolePluginImageProvided() {
4✔
106
                h.addOperand(scheme, hc, handlers.NewKvUIPluginDeploymentHandler)
2✔
107
                h.addOperand(scheme, hc, handlers.NewKvUIProxyDeploymentHandler)
2✔
108
                h.addOperand(scheme, hc, handlers.NewKvUINginxCMHandler)
2✔
109
                h.addOperand(scheme, hc, handlers.NewKvUIPluginCRHandler)
2✔
110
                h.addOperand(scheme, hc, handlers.NewKvUIUserSettingsCMHandler)
2✔
111
                h.addOperand(scheme, hc, handlers.NewKvUIFeaturesCMHandler)
2✔
112
                h.addOperand(scheme, hc, handlers.NewKvUIConfigReaderRoleHandler)
2✔
113
                h.addOperand(scheme, hc, handlers.NewKvUIConfigReaderRoleBindingHandler)
2✔
114

2✔
115
        }
2✔
116
}
117

118
func (h *OperandHandler) GetQuickStartNames() []string {
1✔
119
        return handlers.GetQuickStartNames()
1✔
120
}
1✔
121

122
func (h *OperandHandler) GetImageStreamNames() []string {
1✔
123
        return handlers.GetImageStreamNames()
1✔
124
}
1✔
125

126
func (h *OperandHandler) addOperandObject(handler operands.Operand, hc *hcov1beta1.HyperConverged) {
2✔
127
        var (
2✔
128
                obj client.Object
2✔
129
                err error
2✔
130
        )
2✔
131

2✔
132
        if gh, ok := handler.(operands.CRGetter); ok {
4✔
133
                obj, err = gh.GetFullCr(hc)
2✔
134
        } else {
2✔
UNCOV
135
                err = fmt.Errorf("unknown handler with type %T", handler)
×
UNCOV
136
        }
×
137

138
        if err != nil {
2✔
UNCOV
139
                logger.Error(err, "can't create object")
×
140
        } else {
2✔
141
                h.objects = append(h.objects, obj)
2✔
142
        }
2✔
143
}
144

145
func (h *OperandHandler) addOperands(scheme *runtime.Scheme, hc *hcov1beta1.HyperConverged, getHandlers operands.GetHandlers) {
2✔
146
        handlers, err := getHandlers(logger, h.client, scheme, hc)
2✔
147
        if err != nil {
2✔
UNCOV
148
                logger.Error(err, "can't create handler")
×
149
        } else if len(handlers) > 0 {
3✔
150
                for _, handler := range handlers {
2✔
151
                        h.addOperandObject(handler, hc)
1✔
152
                }
1✔
153
                h.operands = append(h.operands, handlers...)
1✔
154
        }
155
}
156

157
func (h *OperandHandler) addOperand(scheme *runtime.Scheme, hc *hcov1beta1.HyperConverged, getHandler operands.GetHandler) {
2✔
158
        handler, err := getHandler(logger, h.client, scheme, hc)
2✔
159
        if err != nil {
3✔
160
                logger.Error(err, "can't create handler")
1✔
161
                return
1✔
162
        }
1✔
163

164
        h.addOperandObject(handler, hc)
2✔
165

2✔
166
        h.operands = append(h.operands, handler)
2✔
167
}
168

169
func (h *OperandHandler) Ensure(req *common.HcoRequest) error {
2✔
170
        for _, handler := range h.operands {
4✔
171
                res := handler.Ensure(req)
2✔
172
                if res.Err != nil {
4✔
173
                        req.Logger.Error(res.Err, "failed to Ensure an operand")
2✔
174

2✔
175
                        req.ComponentUpgradeInProgress = false
2✔
176
                        req.Conditions.SetStatusCondition(metav1.Condition{
2✔
177
                                Type:               hcov1beta1.ConditionReconcileComplete,
2✔
178
                                Status:             metav1.ConditionFalse,
2✔
179
                                Reason:             reconcileFailed,
2✔
180
                                Message:            fmt.Sprintf("Error while reconciling: %v", res.Err),
2✔
181
                                ObservedGeneration: req.Instance.Generation,
2✔
182
                        })
2✔
183
                        return res.Err
2✔
184
                }
2✔
185

186
                if res.Created {
4✔
187
                        h.eventEmitter.EmitEvent(req.Instance, corev1.EventTypeNormal, "Created", fmt.Sprintf("Created %s %s", res.Type, res.Name))
2✔
188
                } else if res.Updated {
5✔
189
                        h.handleUpdatedOperand(req, res)
1✔
190
                } else if res.Deleted {
4✔
191
                        h.eventEmitter.EmitEvent(req.Instance, corev1.EventTypeNormal, "Killing", fmt.Sprintf("Removed %s %s", res.Type, res.Name))
1✔
192
                }
1✔
193

194
                req.ComponentUpgradeInProgress = req.ComponentUpgradeInProgress && res.UpgradeDone
2✔
195
        }
196
        return nil
2✔
197

198
}
199

200
func (h *OperandHandler) handleUpdatedOperand(req *common.HcoRequest, res *operands.EnsureResult) {
1✔
201
        if !res.Overwritten {
2✔
202
                h.eventEmitter.EmitEvent(req.Instance, corev1.EventTypeNormal, "Updated", fmt.Sprintf("Updated %s %s", res.Type, res.Name))
1✔
203
        } else {
2✔
204
                h.eventEmitter.EmitEvent(req.Instance, corev1.EventTypeWarning, "Overwritten", fmt.Sprintf("Overwritten %s %s", res.Type, res.Name))
1✔
205
                if !req.UpgradeMode {
2✔
206
                        metrics.IncOverwrittenModifications(res.Type, res.Name)
1✔
207
                }
1✔
208
        }
209
}
210

211
func (h *OperandHandler) EnsureDeleted(req *common.HcoRequest) error {
2✔
212

2✔
213
        tCtx, cancel := context.WithTimeout(req.Ctx, deleteTimeOut)
2✔
214
        defer cancel()
2✔
215

2✔
216
        resources := []client.Object{
2✔
217
                handlers.NewKubeVirtWithNameOnly(req.Instance),
2✔
218
                handlers.NewCDIWithNameOnly(req.Instance),
2✔
219
                handlers.NewNetworkAddonsWithNameOnly(req.Instance),
2✔
220
                handlers.NewSSPWithNameOnly(req.Instance),
2✔
221
                handlers.NewConsoleCLIDownload(req.Instance),
2✔
222
                handlers.NewAAQWithNameOnly(req.Instance),
2✔
223
                passt.NewPasstBindingCNISA(req.Instance),
2✔
224
                passt.NewPasstBindingCNIDaemonSetWithNameOnly(req.Instance),
2✔
225
                passt.NewPasstBindingCNINetworkAttachmentDefinition(req.Instance, req.Instance.Namespace),
2✔
226
                passt.NewPasstBindingCNISecurityContextConstraints(req.Instance),
2✔
227
        }
2✔
228

2✔
229
        resources = append(resources, h.objects...)
2✔
230

2✔
231
        eg, egCtx := errgroup.WithContext(tCtx)
2✔
232

2✔
233
        for _, res := range resources {
4✔
234
                func(o client.Object) {
4✔
235
                        eg.Go(func() error {
4✔
236
                                deleted, err := hcoutil.EnsureDeleted(egCtx, h.client, o, req.Instance.Name, req.Logger, false, true, true)
2✔
237
                                if err != nil {
3✔
238
                                        req.Logger.Error(err, "Failed to manually delete objects")
1✔
239
                                        errT := ErrHCOUninstall
1✔
240
                                        errMsg := uninstallHCOErrorMsg
1✔
241
                                        switch o.(type) {
1✔
242
                                        case *kubevirtcorev1.KubeVirt:
1✔
243
                                                errT = ErrVirtUninstall
1✔
244
                                                errMsg = uninstallVirtErrorMsg + err.Error()
1✔
245
                                        case *cdiv1beta1.CDI:
1✔
246
                                                errT = ErrCDIUninstall
1✔
247
                                                errMsg = uninstallCDIErrorMsg + err.Error()
1✔
248
                                        }
249

250
                                        h.eventEmitter.EmitEvent(req.Instance, corev1.EventTypeWarning, errT, errMsg)
1✔
251
                                        return err
1✔
252
                                } else if deleted {
4✔
253
                                        key := client.ObjectKeyFromObject(o)
2✔
254
                                        h.eventEmitter.EmitEvent(req.Instance, corev1.EventTypeNormal, "Killing", fmt.Sprintf("Removed %s %s", o.GetObjectKind().GroupVersionKind().Kind, key.Name))
2✔
255
                                }
2✔
256
                                return nil
2✔
257
                        })
258
                }(res)
259
        }
260

261
        return eg.Wait()
2✔
262
}
263

264
func (h *OperandHandler) Reset() {
1✔
265
        for _, op := range h.operands {
2✔
266
                op.Reset()
1✔
267
        }
1✔
268
}
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