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

kubevirt / hyperconverged-cluster-operator / 16770637622

06 Aug 2025 07:38AM UTC coverage: 74.058% (-1.5%) from 75.516%
16770637622

Pull #3682

github

web-flow
Merge f8a4084a5 into d7fa3159b
Pull Request #3682: Deploy wasp-agent using HCO

94 of 322 new or added lines in 6 files covered. (29.19%)

22 existing lines in 2 files now uncovered.

7271 of 9818 relevant lines covered (74.06%)

1.73 hits per line

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

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

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

8
        waspagent "github.com/kubevirt/hyperconverged-cluster-operator/controllers/handlers/wasp-agent"
9

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

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

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

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

40
var (
41
        logger = logf.Log.WithName("operandHandlerInit")
42
)
43

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

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

2✔
63
        if ci.IsOpenshift() {
4✔
64
                operandList = append(operandList, []operands.Operand{
2✔
65
                        handlers.NewSspHandler(client, scheme),
2✔
66
                        handlers.NewCliDownloadHandler(client, scheme),
2✔
67
                        handlers.NewCliDownloadsRouteHandler(client, scheme),
2✔
68
                        operands.NewServiceHandler(client, scheme, handlers.NewCliDownloadsService),
2✔
69
                        passt.NewPasstServiceAccountHandler(client, scheme),
2✔
70
                        passt.NewPasstSecurityContextConstraintsHandler(client, scheme),
2✔
71
                        waspagent.NewServiceAccountHandler(client, scheme),
2✔
72
                        waspagent.NewClusterRoleHandler(client, scheme),
2✔
73
                        waspagent.NewClusterRoleBindingHandler(client, scheme),
2✔
74
                        waspagent.NewSecurityContextConstraintHandler(client, scheme),
2✔
75
                        waspagent.NewDaemonSetHandler(client, scheme),
2✔
76
                }...)
2✔
77
        }
2✔
78

79
        if ci.IsOpenshift() && ci.IsConsolePluginImageProvided() {
4✔
80
                operandList = append(operandList, handlers.NewConsoleHandler(client))
2✔
81
                operandList = append(operandList, operands.NewServiceHandler(client, scheme, handlers.NewKvUIPluginSvc))
2✔
82
                operandList = append(operandList, operands.NewServiceHandler(client, scheme, handlers.NewKvUIProxySvc))
2✔
83
        }
2✔
84

85
        if ci.IsManagedByOLM() {
4✔
86
                operandList = append(operandList, handlers.NewCsvHandler(client, ci))
2✔
87
        }
2✔
88

89
        return &OperandHandler{
2✔
90
                client:       client,
2✔
91
                operands:     operandList,
2✔
92
                eventEmitter: eventEmitter,
2✔
93
        }
2✔
94
}
95

96
// FirstUseInitiation is a lazy init function
97
// The k8s client is not available when calling to NewOperandHandler.
98
// Initial operations that need to read/write from the cluster can only be done when the client is already working.
99
func (h *OperandHandler) FirstUseInitiation(scheme *runtime.Scheme, ci hcoutil.ClusterInfo, hc *hcov1beta1.HyperConverged) {
2✔
100
        h.objects = make([]client.Object, 0)
2✔
101
        if !ci.IsOpenshift() {
2✔
UNCOV
102
                return
×
UNCOV
103
        }
×
104

105
        for _, fn := range []operands.GetHandlers{
2✔
106
                handlers.GetQuickStartHandlers,
2✔
107
                handlers.GetDashboardHandlers,
2✔
108
                handlers.GetImageStreamHandlers,
2✔
109
        } {
4✔
110
                h.addOperands(scheme, hc, fn)
2✔
111
        }
2✔
112

113
        getHandlerFuncs := []operands.GetHandler{
2✔
114
                handlers.NewVirtioWinCmHandler,
2✔
115
                handlers.NewVirtioWinCmReaderRoleHandler,
2✔
116
                handlers.NewVirtioWinCmReaderRoleBindingHandler,
2✔
117
        }
2✔
118

2✔
119
        if ci.IsConsolePluginImageProvided() {
4✔
120
                getHandlerFuncs = append(getHandlerFuncs,
2✔
121
                        handlers.NewKvUIPluginDeploymentHandler,
2✔
122
                        handlers.NewKvUIProxyDeploymentHandler,
2✔
123
                        handlers.NewKvUINginxCMHandler,
2✔
124
                        handlers.NewKvUIPluginCRHandler,
2✔
125
                        handlers.NewKvUIUserSettingsCMHandler,
2✔
126
                        handlers.NewKvUIFeaturesCMHandler,
2✔
127
                        handlers.NewKvUIConfigReaderRoleHandler,
2✔
128
                        handlers.NewKvUIConfigReaderRoleBindingHandler,
2✔
129
                )
2✔
130

2✔
131
                if common.ShouldDeployNetworkPolicy() {
2✔
132
                        getHandlerFuncs = append(getHandlerFuncs,
×
133
                                handlers.NewKVConsolePluginNetworkPolicyHandler,
×
134
                                handlers.NewKVAPIServerProxyNetworkPolicyHandler,
×
135
                        )
×
UNCOV
136
                }
×
137
        }
138

139
        for _, fn := range getHandlerFuncs {
4✔
140
                h.addOperand(scheme, hc, fn)
2✔
141
        }
2✔
142
}
143

144
func (h *OperandHandler) GetQuickStartNames() []string {
1✔
145
        return handlers.GetQuickStartNames()
1✔
146
}
1✔
147

148
func (h *OperandHandler) GetImageStreamNames() []string {
1✔
149
        return handlers.GetImageStreamNames()
1✔
150
}
1✔
151

152
func (h *OperandHandler) addOperandObject(handler operands.Operand, hc *hcov1beta1.HyperConverged) {
2✔
153
        var (
2✔
154
                obj client.Object
2✔
155
                err error
2✔
156
        )
2✔
157

2✔
158
        if gh, ok := handler.(operands.CRGetter); ok {
4✔
159
                obj, err = gh.GetFullCr(hc)
2✔
160
        } else {
2✔
161
                err = fmt.Errorf("unknown handler with type %T", handler)
×
UNCOV
162
        }
×
163

164
        if err != nil {
2✔
UNCOV
165
                logger.Error(err, "can't create object")
×
166
        } else {
2✔
167
                h.objects = append(h.objects, obj)
2✔
168
        }
2✔
169
}
170

171
func (h *OperandHandler) addOperands(scheme *runtime.Scheme, hc *hcov1beta1.HyperConverged, getHandlers operands.GetHandlers) {
2✔
172
        handlers, err := getHandlers(logger, h.client, scheme, hc)
2✔
173
        if err != nil {
2✔
UNCOV
174
                logger.Error(err, "can't create handler")
×
175
        } else if len(handlers) > 0 {
3✔
176
                for _, handler := range handlers {
2✔
177
                        h.addOperandObject(handler, hc)
1✔
178
                }
1✔
179
                h.operands = append(h.operands, handlers...)
1✔
180
        }
181
}
182

183
func (h *OperandHandler) addOperand(scheme *runtime.Scheme, hc *hcov1beta1.HyperConverged, getHandler operands.GetHandler) {
2✔
184
        handler, err := getHandler(logger, h.client, scheme, hc)
2✔
185
        if err != nil {
3✔
186
                logger.Error(err, "can't create handler")
1✔
187
                return
1✔
188
        }
1✔
189

190
        h.addOperandObject(handler, hc)
2✔
191

2✔
192
        h.operands = append(h.operands, handler)
2✔
193
}
194

195
func (h *OperandHandler) Ensure(req *common.HcoRequest) error {
2✔
196
        for _, handler := range h.operands {
4✔
197
                res := handler.Ensure(req)
2✔
198
                if res.Err != nil {
4✔
199
                        req.Logger.Error(res.Err, "failed to Ensure an operand")
2✔
200

2✔
201
                        req.ComponentUpgradeInProgress = false
2✔
202
                        req.Conditions.SetStatusCondition(metav1.Condition{
2✔
203
                                Type:               hcov1beta1.ConditionReconcileComplete,
2✔
204
                                Status:             metav1.ConditionFalse,
2✔
205
                                Reason:             reconcileFailed,
2✔
206
                                Message:            fmt.Sprintf("Error while reconciling: %v", res.Err),
2✔
207
                                ObservedGeneration: req.Instance.Generation,
2✔
208
                        })
2✔
209
                        return res.Err
2✔
210
                }
2✔
211

212
                if res.Created {
4✔
213
                        h.eventEmitter.EmitEvent(req.Instance, corev1.EventTypeNormal, "Created", fmt.Sprintf("Created %s %s", res.Type, res.Name))
2✔
214
                } else if res.Updated {
5✔
215
                        h.handleUpdatedOperand(req, res)
1✔
216
                } else if res.Deleted {
4✔
217
                        h.eventEmitter.EmitEvent(req.Instance, corev1.EventTypeNormal, "Killing", fmt.Sprintf("Removed %s %s", res.Type, res.Name))
1✔
218
                }
1✔
219

220
                req.ComponentUpgradeInProgress = req.ComponentUpgradeInProgress && res.UpgradeDone
2✔
221
        }
222
        return nil
2✔
223

224
}
225

226
func (h *OperandHandler) handleUpdatedOperand(req *common.HcoRequest, res *operands.EnsureResult) {
1✔
227
        if !res.Overwritten {
2✔
228
                h.eventEmitter.EmitEvent(req.Instance, corev1.EventTypeNormal, "Updated", fmt.Sprintf("Updated %s %s", res.Type, res.Name))
1✔
229
        } else {
2✔
230
                h.eventEmitter.EmitEvent(req.Instance, corev1.EventTypeWarning, "Overwritten", fmt.Sprintf("Overwritten %s %s", res.Type, res.Name))
1✔
231
                if !req.UpgradeMode {
2✔
232
                        metrics.IncOverwrittenModifications(res.Type, res.Name)
1✔
233
                }
1✔
234
        }
235
}
236

237
func (h *OperandHandler) EnsureDeleted(req *common.HcoRequest) error {
2✔
238

2✔
239
        tCtx, cancel := context.WithTimeout(req.Ctx, deleteTimeOut)
2✔
240
        defer cancel()
2✔
241

2✔
242
        resources := []client.Object{
2✔
243
                handlers.NewKubeVirtWithNameOnly(req.Instance),
2✔
244
                handlers.NewCDIWithNameOnly(req.Instance),
2✔
245
                handlers.NewNetworkAddonsWithNameOnly(req.Instance),
2✔
246
                handlers.NewSSPWithNameOnly(req.Instance),
2✔
247
                handlers.NewConsoleCLIDownload(req.Instance),
2✔
248
                handlers.NewAAQWithNameOnly(req.Instance),
2✔
249
                passt.NewPasstBindingCNISA(req.Instance),
2✔
250
                passt.NewPasstBindingCNIDaemonSetWithNameOnly(req.Instance),
2✔
251
                passt.NewPasstBindingCNINetworkAttachmentDefinition(req.Instance),
2✔
252
                passt.NewPasstBindingCNISecurityContextConstraints(req.Instance),
2✔
253
        }
2✔
254

2✔
255
        resources = append(resources, h.objects...)
2✔
256

2✔
257
        eg, egCtx := errgroup.WithContext(tCtx)
2✔
258

2✔
259
        for _, res := range resources {
4✔
260
                func(o client.Object) {
4✔
261
                        eg.Go(func() error {
4✔
262
                                deleted, err := hcoutil.EnsureDeleted(egCtx, h.client, o, req.Instance.Name, req.Logger, false, true, true)
2✔
263
                                if err != nil {
3✔
264
                                        req.Logger.Error(err, "Failed to manually delete objects")
1✔
265
                                        errT := ErrHCOUninstall
1✔
266
                                        errMsg := uninstallHCOErrorMsg
1✔
267
                                        switch o.(type) {
1✔
268
                                        case *kubevirtcorev1.KubeVirt:
1✔
269
                                                errT = ErrVirtUninstall
1✔
270
                                                errMsg = uninstallVirtErrorMsg + err.Error()
1✔
271
                                        case *cdiv1beta1.CDI:
1✔
272
                                                errT = ErrCDIUninstall
1✔
273
                                                errMsg = uninstallCDIErrorMsg + err.Error()
1✔
274
                                        }
275

276
                                        h.eventEmitter.EmitEvent(req.Instance, corev1.EventTypeWarning, errT, errMsg)
1✔
277
                                        return err
1✔
278
                                } else if deleted {
4✔
279
                                        key := client.ObjectKeyFromObject(o)
2✔
280
                                        h.eventEmitter.EmitEvent(req.Instance, corev1.EventTypeNormal, "Killing", fmt.Sprintf("Removed %s %s", o.GetObjectKind().GroupVersionKind().Kind, key.Name))
2✔
281
                                }
2✔
282
                                return nil
2✔
283
                        })
284
                }(res)
285
        }
286

287
        return eg.Wait()
2✔
288
}
289

290
func (h *OperandHandler) Reset() {
1✔
291
        for _, op := range h.operands {
2✔
292
                op.Reset()
1✔
293
        }
1✔
294
}
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