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

kubevirt / hyperconverged-cluster-operator / 14333361311

08 Apr 2025 12:21PM UTC coverage: 72.322%. Remained the same
14333361311

Pull #3391

github

web-flow
Merge 8bcd5654d into 41fbf107a
Pull Request #3391: Move the enableApplicationAwareQuota feature gate to new field

8 of 8 new or added lines in 2 files covered. (100.0%)

9 existing lines in 1 file now uncovered.

6535 of 9036 relevant lines covered (72.32%)

0.95 hits per line

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

91.41
/pkg/webhooks/validator/validator.go
1
package validator
2

3
import (
4
        "context"
5
        "errors"
6
        "fmt"
7
        "net/http"
8
        "reflect"
9
        "strings"
10
        "time"
11

12
        "github.com/go-logr/logr"
13
        openshiftconfigv1 "github.com/openshift/api/config/v1"
14
        "github.com/samber/lo"
15
        xsync "golang.org/x/sync/errgroup"
16
        admissionv1 "k8s.io/api/admission/v1"
17
        apierrors "k8s.io/apimachinery/pkg/api/errors"
18
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19
        "k8s.io/utils/strings/slices"
20
        "sigs.k8s.io/controller-runtime/pkg/client"
21
        "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
22

23
        networkaddonsv1 "github.com/kubevirt/cluster-network-addons-operator/pkg/apis/networkaddonsoperator/v1"
24
        kubevirtcorev1 "kubevirt.io/api/core/v1"
25
        cdiv1beta1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"
26
        sspv1beta2 "kubevirt.io/ssp-operator/api/v1beta2"
27

28
        "github.com/kubevirt/hyperconverged-cluster-operator/api/v1beta1"
29
        "github.com/kubevirt/hyperconverged-cluster-operator/controllers/operands"
30
        hcoutil "github.com/kubevirt/hyperconverged-cluster-operator/pkg/util"
31
)
32

33
const (
34
        updateDryRunTimeOut = time.Second * 3
35
)
36

37
type ValidationWarning struct {
38
        warnings []string
39
}
40

41
func newValidationWarning(warnings []string) *ValidationWarning {
1✔
42
        return &ValidationWarning{
1✔
43
                warnings: warnings,
1✔
44
        }
1✔
45
}
1✔
46

47
func (v *ValidationWarning) Error() string {
×
48
        return ""
×
49
}
×
50

51
func (v *ValidationWarning) Warnings() []string {
×
52
        return v.warnings
×
53
}
×
54

55
type WebhookHandler struct {
56
        logger      logr.Logger
57
        cli         client.Client
58
        namespace   string
59
        isOpenshift bool
60
        decoder     admission.Decoder
61
}
62

63
var hcoTLSConfigCache *openshiftconfigv1.TLSSecurityProfile
64

65
func NewWebhookHandler(logger logr.Logger, cli client.Client, decoder admission.Decoder, namespace string, isOpenshift bool, hcoTLSSecurityProfile *openshiftconfigv1.TLSSecurityProfile) *WebhookHandler {
1✔
66
        hcoTLSConfigCache = hcoTLSSecurityProfile
1✔
67
        return &WebhookHandler{
1✔
68
                logger:      logger,
1✔
69
                cli:         cli,
1✔
70
                namespace:   namespace,
1✔
71
                isOpenshift: isOpenshift,
1✔
72
                decoder:     decoder,
1✔
73
        }
1✔
74
}
1✔
75

76
func (wh *WebhookHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
1✔
77

1✔
78
        ctx = admission.NewContextWithRequest(ctx, req)
1✔
79

1✔
80
        // Get the object in the request
1✔
81
        obj := &v1beta1.HyperConverged{}
1✔
82

1✔
83
        dryRun := req.DryRun != nil && *req.DryRun
1✔
84

1✔
85
        var err error
1✔
86
        switch req.Operation {
1✔
87
        case admissionv1.Create:
1✔
88
                if err := wh.decoder.Decode(req, obj); err != nil {
2✔
89
                        return admission.Errored(http.StatusBadRequest, err)
1✔
90
                }
1✔
91

92
                err = wh.ValidateCreate(ctx, dryRun, obj)
1✔
93
        case admissionv1.Update:
1✔
94
                oldObj := &v1beta1.HyperConverged{}
1✔
95
                if err := wh.decoder.DecodeRaw(req.Object, obj); err != nil {
2✔
96
                        return admission.Errored(http.StatusBadRequest, err)
1✔
97
                }
1✔
98
                if err := wh.decoder.DecodeRaw(req.OldObject, oldObj); err != nil {
2✔
99
                        return admission.Errored(http.StatusBadRequest, err)
1✔
100
                }
1✔
101

102
                err = wh.ValidateUpdate(ctx, dryRun, obj, oldObj)
1✔
103
        case admissionv1.Delete:
1✔
104
                // In reference to PR: https://github.com/kubernetes/kubernetes/pull/76346
1✔
105
                // OldObject contains the object being deleted
1✔
106
                if err := wh.decoder.DecodeRaw(req.OldObject, obj); err != nil {
2✔
107
                        return admission.Errored(http.StatusBadRequest, err)
1✔
108
                }
1✔
109

110
                err = wh.ValidateDelete(ctx, dryRun, obj)
1✔
111
        default:
1✔
112
                return admission.Errored(http.StatusBadRequest, fmt.Errorf("unknown operation request %q", req.Operation))
1✔
113
        }
114

115
        // Check the error message first.
116
        if err != nil {
2✔
117
                var apiStatus apierrors.APIStatus
1✔
118
                if errors.As(err, &apiStatus) {
1✔
119
                        return validationResponseFromStatus(false, apiStatus.Status())
×
120
                }
×
121

122
                var vw *ValidationWarning
1✔
123
                if errors.As(err, &vw) {
1✔
124
                        return admission.Allowed("").WithWarnings(vw.Warnings()...)
×
125
                }
×
126

127
                return admission.Denied(err.Error())
1✔
128
        }
129

130
        // Return allowed if everything succeeded.
131
        return admission.Allowed("")
1✔
132
}
133

134
func (wh *WebhookHandler) ValidateCreate(_ context.Context, dryrun bool, hc *v1beta1.HyperConverged) error {
1✔
135
        wh.logger.Info("Validating create", "name", hc.Name, "namespace:", hc.Namespace)
1✔
136

1✔
137
        if err := wh.validateCertConfig(hc); err != nil {
1✔
138
                return err
×
139
        }
×
140

141
        if hc.Namespace != wh.namespace {
2✔
142
                return fmt.Errorf("invalid namespace for v1beta1.HyperConverged - please use the %s namespace", wh.namespace)
1✔
143
        }
1✔
144

145
        if err := wh.validateDataImportCronTemplates(hc); err != nil {
2✔
146
                return err
1✔
147
        }
1✔
148

149
        if err := wh.validateTLSSecurityProfiles(hc); err != nil {
2✔
150
                return err
1✔
151
        }
1✔
152

153
        if err := wh.validateMediatedDeviceTypes(hc); err != nil {
2✔
154
                return err
1✔
155
        }
1✔
156

157
        if err := wh.validateFeatureGatesOnCreate(hc); err != nil {
2✔
158
                return err
1✔
159
        }
1✔
160

161
        if _, err := operands.NewKubeVirt(hc); err != nil {
2✔
162
                return err
1✔
163
        }
1✔
164

165
        if _, err := operands.NewCDI(hc); err != nil {
2✔
166
                return err
1✔
167
        }
1✔
168

169
        if _, err := operands.NewNetworkAddons(hc); err != nil {
2✔
170
                return err
1✔
171
        }
1✔
172

173
        if _, _, err := operands.NewSSP(hc); err != nil {
2✔
174
                return err
1✔
175
        }
1✔
176

177
        if !dryrun {
2✔
178
                hcoTLSConfigCache = hc.Spec.TLSSecurityProfile
1✔
179
        }
1✔
180

181
        return nil
1✔
182
}
183

184
func (wh *WebhookHandler) getOperands(requested *v1beta1.HyperConverged) (*kubevirtcorev1.KubeVirt, *cdiv1beta1.CDI, *networkaddonsv1.NetworkAddonsConfig, error) {
1✔
185
        if err := wh.validateCertConfig(requested); err != nil {
2✔
186
                return nil, nil, nil, err
1✔
187
        }
1✔
188

189
        kv, err := operands.NewKubeVirt(requested)
1✔
190
        if err != nil {
2✔
191
                return nil, nil, nil, err
1✔
192
        }
1✔
193

194
        cdi, err := operands.NewCDI(requested)
1✔
195
        if err != nil {
2✔
196
                return nil, nil, nil, err
1✔
197
        }
1✔
198

199
        cna, err := operands.NewNetworkAddons(requested)
1✔
200
        if err != nil {
2✔
201
                return nil, nil, nil, err
1✔
202
        }
1✔
203

204
        return kv, cdi, cna, nil
1✔
205
}
206

207
// ValidateUpdate is the ValidateUpdate webhook implementation. It calls all the resources in parallel, to dry-run the
208
// upgrade.
209
func (wh *WebhookHandler) ValidateUpdate(ctx context.Context, dryrun bool, requested *v1beta1.HyperConverged, exists *v1beta1.HyperConverged) error {
1✔
210
        wh.logger.Info("Validating update", "name", requested.Name)
1✔
211

1✔
212
        if err := wh.validateDataImportCronTemplates(requested); err != nil {
1✔
213
                return err
×
214
        }
×
215

216
        if err := wh.validateTLSSecurityProfiles(requested); err != nil {
2✔
217
                return err
1✔
218
        }
1✔
219

220
        if err := wh.validateMediatedDeviceTypes(requested); err != nil {
2✔
221
                return err
1✔
222
        }
1✔
223

224
        if err := wh.validateFeatureGatesOnUpdate(requested, exists); err != nil {
2✔
225
                return err
1✔
226
        }
1✔
227

228
        // If no change is detected in the spec nor the annotations - nothing to validate
229
        if reflect.DeepEqual(exists.Spec, requested.Spec) &&
1✔
230
                reflect.DeepEqual(exists.Annotations, requested.Annotations) {
2✔
231
                return nil
1✔
232
        }
1✔
233

234
        kv, cdi, cna, err := wh.getOperands(requested)
1✔
235
        if err != nil {
2✔
236
                return err
1✔
237
        }
1✔
238

239
        toCtx, cancel := context.WithTimeout(ctx, updateDryRunTimeOut)
1✔
240
        defer cancel()
1✔
241

1✔
242
        eg, egCtx := xsync.WithContext(toCtx)
1✔
243
        opts := &client.UpdateOptions{DryRun: []string{metav1.DryRunAll}}
1✔
244

1✔
245
        resources := []client.Object{
1✔
246
                kv,
1✔
247
                cdi,
1✔
248
                cna,
1✔
249
        }
1✔
250

1✔
251
        if wh.isOpenshift {
2✔
252
                ssp, _, err := operands.NewSSP(requested)
1✔
253
                if err != nil {
2✔
254
                        return err
1✔
255
                }
1✔
256
                resources = append(resources, ssp)
1✔
257
        }
258

259
        for _, obj := range resources {
2✔
260
                func(o client.Object) {
2✔
261
                        eg.Go(func() error {
2✔
262
                                return wh.updateOperatorCr(egCtx, requested, o, opts)
1✔
263
                        })
1✔
264
                }(obj)
265
        }
266

267
        err = eg.Wait()
1✔
268
        if err != nil {
2✔
269
                return err
1✔
270
        }
1✔
271

272
        if !dryrun {
2✔
273
                hcoTLSConfigCache = requested.Spec.TLSSecurityProfile
1✔
274
        }
1✔
275

276
        return nil
1✔
277
}
278

279
func (wh *WebhookHandler) updateOperatorCr(ctx context.Context, hc *v1beta1.HyperConverged, exists client.Object, opts *client.UpdateOptions) error {
1✔
280
        err := hcoutil.GetRuntimeObject(ctx, wh.cli, exists)
1✔
281
        if err != nil {
2✔
282
                wh.logger.Error(err, "failed to get object from kubernetes", "kind", exists.GetObjectKind())
1✔
283
                return err
1✔
284
        }
1✔
285

286
        switch existing := exists.(type) {
1✔
287
        case *kubevirtcorev1.KubeVirt:
1✔
288
                required, err := operands.NewKubeVirt(hc)
1✔
289
                if err != nil {
1✔
290
                        return err
×
291
                }
×
292
                required.Spec.DeepCopyInto(&existing.Spec)
1✔
293

294
        case *cdiv1beta1.CDI:
1✔
295
                required, err := operands.NewCDI(hc)
1✔
296
                if err != nil {
1✔
297
                        return err
×
298
                }
×
299
                required.Spec.DeepCopyInto(&existing.Spec)
1✔
300

301
        case *networkaddonsv1.NetworkAddonsConfig:
1✔
302
                required, err := operands.NewNetworkAddons(hc)
1✔
303
                if err != nil {
1✔
304
                        return err
×
305
                }
×
306
                required.Spec.DeepCopyInto(&existing.Spec)
1✔
307

308
        case *sspv1beta2.SSP:
1✔
309
                required, _, err := operands.NewSSP(hc)
1✔
310
                if err != nil {
1✔
311
                        return err
×
312
                }
×
313
                required.Spec.DeepCopyInto(&existing.Spec)
1✔
314
        }
315

316
        if err = wh.cli.Update(ctx, exists, opts); err != nil {
2✔
317
                wh.logger.Error(err, "failed to dry-run update the object", "kind", exists.GetObjectKind())
1✔
318
                return err
1✔
319
        }
1✔
320

321
        wh.logger.Info("dry-run update the object passed", "kind", exists.GetObjectKind())
1✔
322
        return nil
1✔
323
}
324

325
func (wh *WebhookHandler) ValidateDelete(ctx context.Context, dryrun bool, hc *v1beta1.HyperConverged) error {
1✔
326
        wh.logger.Info("Validating delete", "name", hc.Name, "namespace", hc.Namespace)
1✔
327

1✔
328
        kv := operands.NewKubeVirtWithNameOnly(hc)
1✔
329
        cdi := operands.NewCDIWithNameOnly(hc)
1✔
330

1✔
331
        for _, obj := range []client.Object{
1✔
332
                kv,
1✔
333
                cdi,
1✔
334
        } {
2✔
335
                _, err := hcoutil.EnsureDeleted(ctx, wh.cli, obj, hc.Name, wh.logger, true, false, true)
1✔
336
                if err != nil {
2✔
337
                        wh.logger.Error(err, "Delete validation failed", "GVK", obj.GetObjectKind().GroupVersionKind())
1✔
338
                        return err
1✔
339
                }
1✔
340
        }
341
        if !dryrun {
2✔
342
                hcoTLSConfigCache = nil
1✔
343
        }
1✔
344
        return nil
1✔
345
}
346

347
func (wh *WebhookHandler) validateCertConfig(hc *v1beta1.HyperConverged) error {
1✔
348
        minimalDuration := metav1.Duration{Duration: 10 * time.Minute}
1✔
349

1✔
350
        ccValues := make(map[string]time.Duration)
1✔
351
        ccValues["spec.certConfig.ca.duration"] = hc.Spec.CertConfig.CA.Duration.Duration
1✔
352
        ccValues["spec.certConfig.ca.renewBefore"] = hc.Spec.CertConfig.CA.RenewBefore.Duration
1✔
353
        ccValues["spec.certConfig.server.duration"] = hc.Spec.CertConfig.Server.Duration.Duration
1✔
354
        ccValues["spec.certConfig.server.renewBefore"] = hc.Spec.CertConfig.Server.RenewBefore.Duration
1✔
355

1✔
356
        for key, value := range ccValues {
2✔
357
                if value < minimalDuration.Duration {
2✔
358
                        return fmt.Errorf("%v: value is too small", key)
1✔
359
                }
1✔
360
        }
361

362
        if hc.Spec.CertConfig.CA.Duration.Duration < hc.Spec.CertConfig.CA.RenewBefore.Duration {
2✔
363
                return errors.New("spec.certConfig.ca: duration is smaller than renewBefore")
1✔
364
        }
1✔
365

366
        if hc.Spec.CertConfig.Server.Duration.Duration < hc.Spec.CertConfig.Server.RenewBefore.Duration {
2✔
367
                return errors.New("spec.certConfig.server: duration is smaller than renewBefore")
1✔
368
        }
1✔
369

370
        if hc.Spec.CertConfig.CA.Duration.Duration < hc.Spec.CertConfig.Server.Duration.Duration {
2✔
371
                return errors.New("spec.certConfig: ca.duration is smaller than server.duration")
1✔
372
        }
1✔
373

374
        return nil
1✔
375
}
376

377
func (wh *WebhookHandler) validateDataImportCronTemplates(hc *v1beta1.HyperConverged) error {
1✔
378

1✔
379
        for _, dict := range hc.Spec.DataImportCronTemplates {
2✔
380
                val, ok := dict.Annotations[hcoutil.DataImportCronEnabledAnnotation]
1✔
381
                val = strings.ToLower(val)
1✔
382
                if ok && val != "false" && val != "true" {
2✔
383
                        return fmt.Errorf(`the %s annotation of a dataImportCronTemplate must be either "true" or "false"`, hcoutil.DataImportCronEnabledAnnotation)
1✔
384
                }
1✔
385

386
                enabled := !ok || val == "true"
1✔
387

1✔
388
                if enabled && dict.Spec == nil {
2✔
389
                        return fmt.Errorf("dataImportCronTemplate spec is empty for an enabled DataImportCronTemplate")
1✔
390
                }
1✔
391
        }
392

393
        return nil
1✔
394
}
395

396
func (wh *WebhookHandler) validateTLSSecurityProfiles(hc *v1beta1.HyperConverged) error {
1✔
397
        tlsSP := hc.Spec.TLSSecurityProfile
1✔
398

1✔
399
        if tlsSP == nil || tlsSP.Custom == nil {
2✔
400
                return nil
1✔
401
        }
1✔
402

403
        if !isValidTLSProtocolVersion(tlsSP.Custom.MinTLSVersion) {
2✔
404
                return fmt.Errorf("invalid value for spec.tlsSecurityProfile.custom.minTLSVersion")
1✔
405
        }
1✔
406

407
        if tlsSP.Custom.MinTLSVersion < openshiftconfigv1.VersionTLS13 && !hasRequiredHTTP2Ciphers(tlsSP.Custom.Ciphers) {
2✔
408
                return fmt.Errorf("http2: TLSConfig.CipherSuites is missing an HTTP/2-required AES_128_GCM_SHA256 cipher (need at least one of ECDHE-RSA-AES128-GCM-SHA256 or ECDHE-ECDSA-AES128-GCM-SHA256)")
1✔
409
        } else if tlsSP.Custom.MinTLSVersion == openshiftconfigv1.VersionTLS13 && len(tlsSP.Custom.Ciphers) > 0 {
3✔
410
                return fmt.Errorf("custom ciphers cannot be selected when minTLSVersion is VersionTLS13")
1✔
411
        }
1✔
412

413
        return nil
1✔
414
}
415

416
func (wh *WebhookHandler) validateMediatedDeviceTypes(hc *v1beta1.HyperConverged) error {
1✔
417
        mdc := hc.Spec.MediatedDevicesConfiguration
1✔
418
        if mdc != nil {
2✔
419
                if len(mdc.MediatedDevicesTypes) > 0 && len(mdc.MediatedDeviceTypes) > 0 && !slices.Equal(mdc.MediatedDevicesTypes, mdc.MediatedDeviceTypes) { //nolint SA1019
2✔
420
                        return fmt.Errorf("mediatedDevicesTypes is deprecated, please use mediatedDeviceTypes instead")
1✔
421
                }
1✔
422
                for _, nmdc := range mdc.NodeMediatedDeviceTypes {
2✔
423
                        if len(nmdc.MediatedDevicesTypes) > 0 && len(nmdc.MediatedDeviceTypes) > 0 && !slices.Equal(nmdc.MediatedDevicesTypes, nmdc.MediatedDeviceTypes) { //nolint SA1019
2✔
424
                                return fmt.Errorf("mediatedDevicesTypes is deprecated, please use mediatedDeviceTypes instead")
1✔
425
                        }
1✔
426
                }
427
        }
428
        return nil
1✔
429
}
430

431
const (
432
        fgMovedWarning       = "spec.featureGates.%[1]s is deprecated and ignored. It will removed in a future version; use spec.%[1]s instead"
433
        fgDeprecationWarning = "spec.featureGates.%s is deprecated and ignored. It will be removed in a future version;"
434
)
435

436
func (wh *WebhookHandler) validateFeatureGatesOnCreate(hc *v1beta1.HyperConverged) error {
1✔
437
        warnings := wh.validateDeprecatedFeatureGates(hc)
1✔
438
        warnings = validateOldFGOnCreate(warnings, hc)
1✔
439

1✔
440
        if len(warnings) > 0 {
2✔
441
                return newValidationWarning(warnings)
1✔
442
        }
1✔
443

444
        return nil
1✔
445
}
446

447
func (wh *WebhookHandler) validateFeatureGatesOnUpdate(requested, exists *v1beta1.HyperConverged) error {
1✔
448
        warnings := wh.validateDeprecatedFeatureGates(requested)
1✔
449
        warnings = validateOldFGOnUpdate(warnings, requested, exists)
1✔
450

1✔
451
        if len(warnings) > 0 {
2✔
452
                return newValidationWarning(warnings)
1✔
453
        }
1✔
454

455
        return nil
1✔
456
}
457

458
func (wh *WebhookHandler) validateDeprecatedFeatureGates(hc *v1beta1.HyperConverged) []string {
1✔
459
        var warnings []string
1✔
460

1✔
461
        //nolint:staticcheck
1✔
462
        if hc.Spec.FeatureGates.WithHostPassthroughCPU != nil {
2✔
463
                warnings = append(warnings, fmt.Sprintf(fgDeprecationWarning, "withHostPassthroughCPU"))
1✔
464
        }
1✔
465

466
        //nolint:staticcheck
467
        if hc.Spec.FeatureGates.DeployTektonTaskResources != nil {
2✔
468
                warnings = append(warnings, fmt.Sprintf(fgDeprecationWarning, "deployTektonTaskResources"))
1✔
469
        }
1✔
470

471
        //nolint:staticcheck
472
        if hc.Spec.FeatureGates.NonRoot != nil {
2✔
473
                warnings = append(warnings, fmt.Sprintf(fgDeprecationWarning, "nonRoot"))
1✔
474
        }
1✔
475

476
        //nolint:staticcheck
477
        if hc.Spec.FeatureGates.EnableManagedTenantQuota != nil {
2✔
478
                warnings = append(warnings, fmt.Sprintf(fgDeprecationWarning, "enableManagedTenantQuota"))
1✔
479
        }
1✔
480

481
        return warnings
1✔
482
}
483

484
func validateOldFGOnCreate(warnings []string, hc *v1beta1.HyperConverged) []string {
1✔
485
        //nolint:staticcheck
1✔
486
        if hc.Spec.FeatureGates.EnableCommonBootImageImport != nil {
2✔
487
                warnings = append(warnings, fmt.Sprintf(fgMovedWarning, "enableCommonBootImageImport"))
1✔
488
        }
1✔
489

490
        //nolint:staticcheck
491
        if hc.Spec.FeatureGates.DeployVMConsoleProxy != nil {
2✔
492
                warnings = append(warnings, fmt.Sprintf(fgMovedWarning, "deployVmConsoleProxy"))
1✔
493
        }
1✔
494

495
        //nolint:staticcheck
496
        if hc.Spec.FeatureGates.DeployKubeSecondaryDNS != nil {
2✔
497
                warnings = append(warnings, fmt.Sprintf(fgMovedWarning, "deployKubeSecondaryDNS"))
1✔
498
        }
1✔
499

500
        return warnings
1✔
501
}
502

503
func validateOldFGOnUpdate(warnings []string, hc, prevHC *v1beta1.HyperConverged) []string {
1✔
504
        //nolint:staticcheck
1✔
505
        if oldFGChanged(hc.Spec.FeatureGates.EnableCommonBootImageImport, prevHC.Spec.FeatureGates.EnableCommonBootImageImport) {
2✔
506
                warnings = append(warnings, fmt.Sprintf(fgMovedWarning, "enableCommonBootImageImport"))
1✔
507
        }
1✔
508

509
        //nolint:staticcheck
510
        if oldFGChanged(hc.Spec.FeatureGates.DeployVMConsoleProxy, prevHC.Spec.FeatureGates.DeployVMConsoleProxy) {
2✔
511
                warnings = append(warnings, fmt.Sprintf(fgMovedWarning, "deployVmConsoleProxy"))
1✔
512
        }
1✔
513

514
        //nolint:staticcheck
515
        if oldFGChanged(hc.Spec.FeatureGates.DeployKubeSecondaryDNS, prevHC.Spec.FeatureGates.DeployKubeSecondaryDNS) {
2✔
516
                warnings = append(warnings, fmt.Sprintf(fgMovedWarning, "deployKubeSecondaryDNS"))
1✔
517
        }
1✔
518

519
        return warnings
1✔
520
}
521

522
func oldFGChanged(newFG, prevFG *bool) bool {
1✔
523
        return newFG != nil && (prevFG == nil || *newFG != *prevFG)
1✔
524
}
1✔
525

526
func hasRequiredHTTP2Ciphers(ciphers []string) bool {
1✔
527
        var requiredHTTP2Ciphers = []string{
1✔
528
                "ECDHE-RSA-AES128-GCM-SHA256",
1✔
529
                "ECDHE-ECDSA-AES128-GCM-SHA256",
1✔
530
        }
1✔
531

1✔
532
        // lo.Some returns true if at least 1 element of a subset is contained into a collection
1✔
533
        return lo.Some[string](requiredHTTP2Ciphers, ciphers)
1✔
534
}
1✔
535

536
// validationResponseFromStatus returns a response for admitting a request with provided Status object.
UNCOV
537
func validationResponseFromStatus(allowed bool, status metav1.Status) admission.Response {
×
UNCOV
538
        resp := admission.Response{
×
UNCOV
539
                AdmissionResponse: admissionv1.AdmissionResponse{
×
UNCOV
540
                        Allowed: allowed,
×
UNCOV
541
                        Result:  &status,
×
UNCOV
542
                },
×
UNCOV
543
        }
×
UNCOV
544
        return resp
×
UNCOV
545
}
×
546

547
func SelectCipherSuitesAndMinTLSVersion() ([]string, openshiftconfigv1.TLSProtocolVersion) {
1✔
548
        ci := hcoutil.GetClusterInfo()
1✔
549
        profile := ci.GetTLSSecurityProfile(hcoTLSConfigCache)
1✔
550

1✔
551
        if profile.Custom != nil {
1✔
552
                return profile.Custom.Ciphers, profile.Custom.MinTLSVersion
×
553
        }
×
554

555
        return openshiftconfigv1.TLSProfiles[profile.Type].Ciphers, openshiftconfigv1.TLSProfiles[profile.Type].MinTLSVersion
1✔
556
}
557

558
func isValidTLSProtocolVersion(pv openshiftconfigv1.TLSProtocolVersion) bool {
1✔
559
        switch pv {
1✔
560
        case
561
                openshiftconfigv1.VersionTLS10,
562
                openshiftconfigv1.VersionTLS11,
563
                openshiftconfigv1.VersionTLS12,
564
                openshiftconfigv1.VersionTLS13:
1✔
565
                return true
1✔
566
        }
567
        return false
1✔
568
}
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