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

kubevirt / hyperconverged-cluster-operator / 16771251937

06 Aug 2025 08:08AM UTC coverage: 74.058% (-1.5%) from 75.516%
16771251937

Pull #3682

github

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

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

18 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
        "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
        waspagent "github.com/kubevirt/hyperconverged-cluster-operator/controllers/handlers/wasp-agent"
23
        "github.com/kubevirt/hyperconverged-cluster-operator/controllers/operands"
24
        "github.com/kubevirt/hyperconverged-cluster-operator/pkg/monitoring/hyperconverged/metrics"
25
        hcoutil "github.com/kubevirt/hyperconverged-cluster-operator/pkg/util"
26
)
27

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

223
}
224

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

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

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

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

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

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

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

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

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

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