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

kubevirt / hyperconverged-cluster-operator / 16749539234

05 Aug 2025 12:06PM UTC coverage: 73.84% (-1.5%) from 75.316%
16749539234

Pull #3682

github

web-flow
Merge 1c014d8b9 into 6b1e522ae
Pull Request #3682: Deploy wasp-agent using HCO

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

19 existing lines in 2 files now uncovered.

7130 of 9656 relevant lines covered (73.84%)

1.74 hits per line

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

97.8
/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() {
4✔
102
                h.addOperands(scheme, hc, handlers.GetQuickStartHandlers)
2✔
103
                h.addOperands(scheme, hc, handlers.GetDashboardHandlers)
2✔
104
                h.addOperands(scheme, hc, handlers.GetImageStreamHandlers)
2✔
105
                h.addOperand(scheme, hc, handlers.NewVirtioWinCmHandler)
2✔
106
                h.addOperand(scheme, hc, handlers.NewVirtioWinCmReaderRoleHandler)
2✔
107
                h.addOperand(scheme, hc, handlers.NewVirtioWinCmReaderRoleBindingHandler)
2✔
108
        }
2✔
109

110
        if ci.IsOpenshift() && ci.IsConsolePluginImageProvided() {
4✔
111
                h.addOperand(scheme, hc, handlers.NewKvUIPluginDeploymentHandler)
2✔
112
                h.addOperand(scheme, hc, handlers.NewKvUIProxyDeploymentHandler)
2✔
113
                h.addOperand(scheme, hc, handlers.NewKvUINginxCMHandler)
2✔
114
                h.addOperand(scheme, hc, handlers.NewKvUIPluginCRHandler)
2✔
115
                h.addOperand(scheme, hc, handlers.NewKvUIUserSettingsCMHandler)
2✔
116
                h.addOperand(scheme, hc, handlers.NewKvUIFeaturesCMHandler)
2✔
117
                h.addOperand(scheme, hc, handlers.NewKvUIConfigReaderRoleHandler)
2✔
118
                h.addOperand(scheme, hc, handlers.NewKvUIConfigReaderRoleBindingHandler)
2✔
119

2✔
120
        }
2✔
121
}
122

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

127
func (h *OperandHandler) GetImageStreamNames() []string {
1✔
128
        return handlers.GetImageStreamNames()
1✔
129
}
1✔
130

131
func (h *OperandHandler) addOperandObject(handler operands.Operand, hc *hcov1beta1.HyperConverged) {
2✔
132
        var (
2✔
133
                obj client.Object
2✔
134
                err error
2✔
135
        )
2✔
136

2✔
137
        if gh, ok := handler.(operands.CRGetter); ok {
4✔
138
                obj, err = gh.GetFullCr(hc)
2✔
139
        } else {
2✔
140
                err = fmt.Errorf("unknown handler with type %T", handler)
×
UNCOV
141
        }
×
142

143
        if err != nil {
2✔
UNCOV
144
                logger.Error(err, "can't create object")
×
145
        } else {
2✔
146
                h.objects = append(h.objects, obj)
2✔
147
        }
2✔
148
}
149

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

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

169
        h.addOperandObject(handler, hc)
2✔
170

2✔
171
        h.operands = append(h.operands, handler)
2✔
172
}
173

174
func (h *OperandHandler) Ensure(req *common.HcoRequest) error {
2✔
175
        for _, handler := range h.operands {
4✔
176
                res := handler.Ensure(req)
2✔
177
                if res.Err != nil {
4✔
178
                        req.Logger.Error(res.Err, "failed to Ensure an operand")
2✔
179

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

191
                if res.Created {
4✔
192
                        h.eventEmitter.EmitEvent(req.Instance, corev1.EventTypeNormal, "Created", fmt.Sprintf("Created %s %s", res.Type, res.Name))
2✔
193
                } else if res.Updated {
5✔
194
                        h.handleUpdatedOperand(req, res)
1✔
195
                } else if res.Deleted {
4✔
196
                        h.eventEmitter.EmitEvent(req.Instance, corev1.EventTypeNormal, "Killing", fmt.Sprintf("Removed %s %s", res.Type, res.Name))
1✔
197
                }
1✔
198

199
                req.ComponentUpgradeInProgress = req.ComponentUpgradeInProgress && res.UpgradeDone
2✔
200
        }
201
        return nil
2✔
202

203
}
204

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

216
func (h *OperandHandler) EnsureDeleted(req *common.HcoRequest) error {
2✔
217

2✔
218
        tCtx, cancel := context.WithTimeout(req.Ctx, deleteTimeOut)
2✔
219
        defer cancel()
2✔
220

2✔
221
        resources := []client.Object{
2✔
222
                handlers.NewKubeVirtWithNameOnly(req.Instance),
2✔
223
                handlers.NewCDIWithNameOnly(req.Instance),
2✔
224
                handlers.NewNetworkAddonsWithNameOnly(req.Instance),
2✔
225
                handlers.NewSSPWithNameOnly(req.Instance),
2✔
226
                handlers.NewConsoleCLIDownload(req.Instance),
2✔
227
                handlers.NewAAQWithNameOnly(req.Instance),
2✔
228
                passt.NewPasstBindingCNISA(req.Instance),
2✔
229
                passt.NewPasstBindingCNIDaemonSetWithNameOnly(req.Instance),
2✔
230
                passt.NewPasstBindingCNINetworkAttachmentDefinition(req.Instance),
2✔
231
                passt.NewPasstBindingCNISecurityContextConstraints(req.Instance),
2✔
232
        }
2✔
233

2✔
234
        resources = append(resources, h.objects...)
2✔
235

2✔
236
        eg, egCtx := errgroup.WithContext(tCtx)
2✔
237

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

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

266
        return eg.Wait()
2✔
267
}
268

269
func (h *OperandHandler) Reset() {
1✔
270
        for _, op := range h.operands {
2✔
271
                op.Reset()
1✔
272
        }
1✔
273
}
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