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

kubevirt / hyperconverged-cluster-operator / 21356570114

26 Jan 2026 11:48AM UTC coverage: 75.948% (-0.09%) from 76.038%
21356570114

Pull #3977

github

web-flow
Merge 335ae0e82 into 4ce018c73
Pull Request #3977: WIP: POC: add api v1

380 of 558 new or added lines in 8 files covered. (68.1%)

9 existing lines in 1 file now uncovered.

8753 of 11525 relevant lines covered (75.95%)

1.77 hits per line

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

86.13
/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
        corev1 "k8s.io/api/core/v1"
18
        apierrors "k8s.io/apimachinery/pkg/api/errors"
19
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20
        "k8s.io/component-helpers/scheduling/corev1/nodeaffinity"
21
        "sigs.k8s.io/controller-runtime/pkg/client"
22
        "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
23

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

29
        hcov1 "github.com/kubevirt/hyperconverged-cluster-operator/api/v1"
30
        hcov1beta1 "github.com/kubevirt/hyperconverged-cluster-operator/api/v1beta1"
31
        "github.com/kubevirt/hyperconverged-cluster-operator/controllers/handlers"
32
        "github.com/kubevirt/hyperconverged-cluster-operator/pkg/nodeinfo"
33
        hcoutil "github.com/kubevirt/hyperconverged-cluster-operator/pkg/util"
34
)
35

36
const (
37
        updateDryRunTimeOut = time.Second * 3
38
)
39

40
type ValidationWarning struct {
41
        warnings []string
42
}
43

44
func newValidationWarning(warnings []string) *ValidationWarning {
1✔
45
        return &ValidationWarning{
1✔
46
                warnings: warnings,
1✔
47
        }
1✔
48
}
1✔
49

50
func (v *ValidationWarning) Error() string {
×
51
        return ""
×
52
}
×
53

54
func (v *ValidationWarning) Warnings() []string {
×
55
        return v.warnings
×
56
}
×
57

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

66
const decodeErrorMsg = "failed to decode the request"
67

68
var (
69
        hcoTLSConfigCache *openshiftconfigv1.TLSSecurityProfile
70
        errDecode         = errors.New(decodeErrorMsg)
71
)
72

73
func NewWebhookHandler(logger logr.Logger, cli client.Client, decoder admission.Decoder, namespace string, isOpenshift bool, hcoTLSSecurityProfile *openshiftconfigv1.TLSSecurityProfile) *WebhookHandler {
2✔
74
        hcoTLSConfigCache = hcoTLSSecurityProfile
2✔
75
        return &WebhookHandler{
2✔
76
                logger:      logger,
2✔
77
                cli:         cli,
2✔
78
                namespace:   namespace,
2✔
79
                isOpenshift: isOpenshift,
2✔
80
                decoder:     decoder,
2✔
81
        }
2✔
82
}
2✔
83

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

1✔
86
        ctx = admission.NewContextWithRequest(ctx, req)
1✔
87
        logger := logr.FromContextOrDiscard(ctx)
1✔
88

1✔
89
        // Get the object in the request
1✔
90
        obj := &hcov1beta1.HyperConverged{}
1✔
91

1✔
92
        dryRun := req.DryRun != nil && *req.DryRun
1✔
93

1✔
94
        var err error
1✔
95
        switch req.Operation {
1✔
96
        case admissionv1.Create:
1✔
97
                v1obj := &hcov1.HyperConverged{}
1✔
98
                if err = wh.decoder.Decode(req, v1obj); err != nil {
2✔
99
                        logger.Error(err, decodeErrorMsg)
1✔
100
                        return admission.Errored(http.StatusBadRequest, errDecode)
1✔
101
                }
1✔
102

103
                if err = obj.ConvertFrom(v1obj); err != nil {
1✔
NEW
104
                        logger.Error(err, decodeErrorMsg)
×
UNCOV
105
                        return admission.Errored(http.StatusBadRequest, err)
×
UNCOV
106
                }
×
107

108
                err = wh.ValidateCreate(ctx, dryRun, obj)
1✔
109
        case admissionv1.Update:
1✔
110
                v1obj := &hcov1.HyperConverged{}
1✔
111
                if err = wh.decoder.DecodeRaw(req.Object, v1obj); err != nil {
2✔
112
                        logger.Error(err, decodeErrorMsg)
1✔
113
                        return admission.Errored(http.StatusBadRequest, errDecode)
1✔
114
                }
1✔
115

116
                if err = obj.ConvertFrom(v1obj); err != nil {
1✔
NEW
117
                        logger.Error(err, decodeErrorMsg)
×
UNCOV
118
                        return admission.Errored(http.StatusBadRequest, err)
×
UNCOV
119
                }
×
120

121
                oldObj := &hcov1beta1.HyperConverged{}
1✔
122
                v1OldObj := &hcov1.HyperConverged{}
1✔
123

1✔
124
                if err = wh.decoder.DecodeRaw(req.OldObject, v1OldObj); err != nil {
2✔
125
                        logger.Error(err, decodeErrorMsg)
1✔
126
                        return admission.Errored(http.StatusBadRequest, errDecode)
1✔
127
                }
1✔
128

129
                if err = oldObj.ConvertFrom(v1OldObj); err != nil {
1✔
NEW
130
                        logger.Error(err, decodeErrorMsg)
×
UNCOV
131
                        return admission.Errored(http.StatusBadRequest, err)
×
UNCOV
132
                }
×
133

134
                err = wh.ValidateUpdate(ctx, dryRun, obj, oldObj)
1✔
135
        case admissionv1.Delete:
1✔
136
                // In reference to PR: https://github.com/kubernetes/kubernetes/pull/76346
1✔
137
                // OldObject contains the object being deleted
1✔
138

1✔
139
                v1obj := &hcov1.HyperConverged{}
1✔
140

1✔
141
                if err = wh.decoder.DecodeRaw(req.OldObject, v1obj); err != nil {
2✔
142
                        logger.Error(err, decodeErrorMsg)
1✔
143
                        return admission.Errored(http.StatusBadRequest, errDecode)
1✔
144
                }
1✔
145

146
                if err = obj.ConvertFrom(v1obj); err != nil {
1✔
NEW
147
                        logger.Error(err, decodeErrorMsg)
×
UNCOV
148
                        return admission.Errored(http.StatusBadRequest, err)
×
UNCOV
149
                }
×
150

151
                err = wh.ValidateDelete(ctx, dryRun, obj)
1✔
152
        default:
1✔
153
                return admission.Errored(http.StatusBadRequest, fmt.Errorf("unknown operation request %q", req.Operation))
1✔
154
        }
155

156
        // Check the error message first.
157
        return getAdmissionResponse(err)
1✔
158
}
159

160
func getAdmissionResponse(err error) admission.Response {
1✔
161
        if err == nil {
2✔
162
                return admission.Allowed("")
1✔
163
        }
1✔
NEW
164
        var apiStatus apierrors.APIStatus
×
NEW
165
        if errors.As(err, &apiStatus) {
×
NEW
166
                return validationResponseFromStatus(false, apiStatus.Status())
×
NEW
167
        }
×
168

NEW
169
        var vw *ValidationWarning
×
NEW
170
        if errors.As(err, &vw) {
×
NEW
171
                return admission.Allowed("").WithWarnings(vw.Warnings()...)
×
UNCOV
172
        }
×
173

NEW
174
        return admission.Denied(err.Error())
×
175
}
176

177
func (wh *WebhookHandler) ValidateCreate(_ context.Context, dryrun bool, hc *hcov1beta1.HyperConverged) error {
1✔
178
        wh.logger.Info("Validating create", "name", hc.Name, "namespace:", hc.Namespace)
1✔
179

1✔
180
        if err := wh.validateCertConfig(hc); err != nil {
1✔
181
                return err
×
182
        }
×
183

184
        if err := wh.validateDataImportCronTemplates(hc); err != nil {
2✔
185
                return err
1✔
186
        }
1✔
187

188
        if err := wh.validateTLSSecurityProfiles(hc); err != nil {
2✔
189
                return err
1✔
190
        }
1✔
191

192
        if err := wh.validateTuningPolicy(hc); err != nil {
2✔
193
                return err
1✔
194
        }
1✔
195

196
        if err := wh.validateAffinity(hc); err != nil {
2✔
197
                return err
1✔
198
        }
1✔
199

200
        if _, err := handlers.NewKubeVirt(hc); err != nil {
2✔
201
                return err
1✔
202
        }
1✔
203

204
        if _, err := handlers.NewCDI(hc); err != nil {
2✔
205
                return err
1✔
206
        }
1✔
207

208
        if _, err := handlers.NewNetworkAddons(hc); err != nil {
2✔
209
                return err
1✔
210
        }
1✔
211

212
        if _, _, err := handlers.NewSSP(hc); err != nil {
2✔
213
                return err
1✔
214
        }
1✔
215

216
        if !dryrun {
2✔
217
                hcoTLSConfigCache = hc.Spec.TLSSecurityProfile
1✔
218
        }
1✔
219

220
        return nil
1✔
221
}
222

223
func (wh *WebhookHandler) getOperands(requested *hcov1beta1.HyperConverged) (*kubevirtcorev1.KubeVirt, *cdiv1beta1.CDI, *networkaddonsv1.NetworkAddonsConfig, error) {
1✔
224
        if err := wh.validateCertConfig(requested); err != nil {
2✔
225
                return nil, nil, nil, err
1✔
226
        }
1✔
227

228
        kv, err := handlers.NewKubeVirt(requested)
1✔
229
        if err != nil {
2✔
230
                return nil, nil, nil, err
1✔
231
        }
1✔
232

233
        cdi, err := handlers.NewCDI(requested)
1✔
234
        if err != nil {
2✔
235
                return nil, nil, nil, err
1✔
236
        }
1✔
237

238
        cna, err := handlers.NewNetworkAddons(requested)
1✔
239
        if err != nil {
2✔
240
                return nil, nil, nil, err
1✔
241
        }
1✔
242

243
        return kv, cdi, cna, nil
1✔
244
}
245

246
// ValidateUpdate is the ValidateUpdate webhook implementation. It calls all the resources in parallel, to dry-run the
247
// upgrade.
248
func (wh *WebhookHandler) ValidateUpdate(ctx context.Context, dryrun bool, requested *hcov1beta1.HyperConverged, exists *hcov1beta1.HyperConverged) error {
1✔
249
        wh.logger.Info("Validating update", "name", requested.Name)
1✔
250

1✔
251
        if err := wh.validateDataImportCronTemplates(requested); err != nil {
1✔
252
                return err
×
253
        }
×
254

255
        if err := wh.validateTLSSecurityProfiles(requested); err != nil {
2✔
256
                return err
1✔
257
        }
1✔
258

259
        if err := wh.validateTuningPolicy(requested); err != nil {
2✔
260
                return err
1✔
261
        }
1✔
262

263
        if err := wh.validateAffinity(requested); err != nil {
1✔
264
                return err
×
265
        }
×
266

267
        // If no change is detected in the spec nor the annotations - nothing to validate
268
        if reflect.DeepEqual(exists.Spec, requested.Spec) &&
1✔
269
                reflect.DeepEqual(exists.Annotations, requested.Annotations) {
2✔
270
                return nil
1✔
271
        }
1✔
272

273
        kv, cdi, cna, err := wh.getOperands(requested)
1✔
274
        if err != nil {
2✔
275
                return err
1✔
276
        }
1✔
277

278
        toCtx, cancel := context.WithTimeout(ctx, updateDryRunTimeOut)
1✔
279
        defer cancel()
1✔
280

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

1✔
284
        resources := []client.Object{
1✔
285
                kv,
1✔
286
                cdi,
1✔
287
                cna,
1✔
288
        }
1✔
289

1✔
290
        if wh.isOpenshift {
2✔
291
                origGetControlPlaneArchitectures := nodeinfo.GetControlPlaneArchitectures
1✔
292
                origGetWorkloadsArchitectures := nodeinfo.GetWorkloadsArchitectures
1✔
293
                defer func() {
2✔
294
                        nodeinfo.GetControlPlaneArchitectures = origGetControlPlaneArchitectures
1✔
295
                        nodeinfo.GetWorkloadsArchitectures = origGetWorkloadsArchitectures
1✔
296
                }()
1✔
297

298
                nodeinfo.GetControlPlaneArchitectures = func() []string {
2✔
299
                        return requested.Status.NodeInfo.ControlPlaneArchitectures
1✔
300
                }
1✔
301
                nodeinfo.GetWorkloadsArchitectures = func() []string {
2✔
302
                        return requested.Status.NodeInfo.WorkloadsArchitectures
1✔
303
                }
1✔
304

305
                ssp, _, err := handlers.NewSSP(requested)
1✔
306
                if err != nil {
2✔
307
                        return err
1✔
308
                }
1✔
309
                resources = append(resources, ssp)
1✔
310
        }
311

312
        for _, obj := range resources {
2✔
313
                func(o client.Object) {
2✔
314
                        eg.Go(func() error {
2✔
315
                                return wh.updateOperatorCr(egCtx, requested, o, opts)
1✔
316
                        })
1✔
317
                }(obj)
318
        }
319

320
        err = eg.Wait()
1✔
321
        if err != nil {
2✔
322
                return err
1✔
323
        }
1✔
324

325
        if !dryrun {
2✔
326
                hcoTLSConfigCache = requested.Spec.TLSSecurityProfile
1✔
327
        }
1✔
328

329
        return nil
1✔
330
}
331

332
func (wh *WebhookHandler) updateOperatorCr(ctx context.Context, hc *hcov1beta1.HyperConverged, exists client.Object, opts *client.UpdateOptions) error {
1✔
333
        err := hcoutil.GetRuntimeObject(ctx, wh.cli, exists)
1✔
334
        if err != nil {
2✔
335
                wh.logger.Error(err, "failed to get object from kubernetes", "kind", exists.GetObjectKind())
1✔
336
                return err
1✔
337
        }
1✔
338

339
        switch existing := exists.(type) {
1✔
340
        case *kubevirtcorev1.KubeVirt:
1✔
341
                required, err := handlers.NewKubeVirt(hc)
1✔
342
                if err != nil {
1✔
343
                        return err
×
344
                }
×
345
                required.Spec.DeepCopyInto(&existing.Spec)
1✔
346

347
        case *cdiv1beta1.CDI:
1✔
348
                required, err := handlers.NewCDI(hc)
1✔
349
                if err != nil {
1✔
350
                        return err
×
351
                }
×
352
                required.Spec.DeepCopyInto(&existing.Spec)
1✔
353

354
        case *networkaddonsv1.NetworkAddonsConfig:
1✔
355
                required, err := handlers.NewNetworkAddons(hc)
1✔
356
                if err != nil {
1✔
357
                        return err
×
358
                }
×
359
                required.Spec.DeepCopyInto(&existing.Spec)
1✔
360

361
        case *sspv1beta3.SSP:
1✔
362
                required, _, err := handlers.NewSSP(hc)
1✔
363
                if err != nil {
1✔
364
                        return err
×
365
                }
×
366
                required.Spec.DeepCopyInto(&existing.Spec)
1✔
367
        }
368

369
        if err = wh.cli.Update(ctx, exists, opts); err != nil {
2✔
370
                wh.logger.Error(err, "failed to dry-run update the object", "kind", exists.GetObjectKind())
1✔
371
                return err
1✔
372
        }
1✔
373

374
        wh.logger.Info("dry-run update the object passed", "kind", exists.GetObjectKind())
1✔
375
        return nil
1✔
376
}
377

378
func (wh *WebhookHandler) ValidateDelete(ctx context.Context, dryrun bool, hc *hcov1beta1.HyperConverged) error {
1✔
379
        wh.logger.Info("Validating delete", "name", hc.Name, "namespace", hc.Namespace)
1✔
380

1✔
381
        kv := handlers.NewKubeVirtWithNameOnly(hc)
1✔
382
        cdi := handlers.NewCDIWithNameOnly(hc)
1✔
383

1✔
384
        for _, obj := range []client.Object{
1✔
385
                kv,
1✔
386
                cdi,
1✔
387
        } {
2✔
388
                _, err := hcoutil.EnsureDeleted(ctx, wh.cli, obj, hc.Name, wh.logger, true, false, true)
1✔
389
                if err != nil {
2✔
390
                        wh.logger.Error(err, "Delete validation failed", "GVK", obj.GetObjectKind().GroupVersionKind())
1✔
391
                        return err
1✔
392
                }
1✔
393
        }
394
        if !dryrun {
2✔
395
                hcoTLSConfigCache = nil
1✔
396
        }
1✔
397
        return nil
1✔
398
}
399

400
func (wh *WebhookHandler) validateCertConfig(hc *hcov1beta1.HyperConverged) error {
1✔
401
        minimalDuration := metav1.Duration{Duration: 10 * time.Minute}
1✔
402

1✔
403
        ccValues := make(map[string]time.Duration)
1✔
404
        ccValues["spec.certConfig.ca.duration"] = hc.Spec.CertConfig.CA.Duration.Duration
1✔
405
        ccValues["spec.certConfig.ca.renewBefore"] = hc.Spec.CertConfig.CA.RenewBefore.Duration
1✔
406
        ccValues["spec.certConfig.server.duration"] = hc.Spec.CertConfig.Server.Duration.Duration
1✔
407
        ccValues["spec.certConfig.server.renewBefore"] = hc.Spec.CertConfig.Server.RenewBefore.Duration
1✔
408

1✔
409
        for key, value := range ccValues {
2✔
410
                if value < minimalDuration.Duration {
2✔
411
                        return fmt.Errorf("%v: value is too small", key)
1✔
412
                }
1✔
413
        }
414

415
        if hc.Spec.CertConfig.CA.Duration.Duration < hc.Spec.CertConfig.CA.RenewBefore.Duration {
2✔
416
                return errors.New("spec.certConfig.ca: duration is smaller than renewBefore")
1✔
417
        }
1✔
418

419
        if hc.Spec.CertConfig.Server.Duration.Duration < hc.Spec.CertConfig.Server.RenewBefore.Duration {
2✔
420
                return errors.New("spec.certConfig.server: duration is smaller than renewBefore")
1✔
421
        }
1✔
422

423
        if hc.Spec.CertConfig.CA.Duration.Duration < hc.Spec.CertConfig.Server.Duration.Duration {
2✔
424
                return errors.New("spec.certConfig: ca.duration is smaller than server.duration")
1✔
425
        }
1✔
426

427
        return nil
1✔
428
}
429

430
func (wh *WebhookHandler) validateDataImportCronTemplates(hc *hcov1beta1.HyperConverged) error {
1✔
431

1✔
432
        for _, dict := range hc.Spec.DataImportCronTemplates {
2✔
433
                val, ok := dict.Annotations[hcoutil.DataImportCronEnabledAnnotation]
1✔
434
                val = strings.ToLower(val)
1✔
435
                if ok && val != "false" && val != "true" {
2✔
436
                        return fmt.Errorf(`the %s annotation of a dataImportCronTemplate must be either "true" or "false"`, hcoutil.DataImportCronEnabledAnnotation)
1✔
437
                }
1✔
438

439
                enabled := !ok || val == "true"
1✔
440

1✔
441
                if enabled && dict.Spec == nil {
2✔
442
                        return fmt.Errorf("dataImportCronTemplate spec is empty for an enabled DataImportCronTemplate")
1✔
443
                }
1✔
444
        }
445

446
        return nil
1✔
447
}
448

449
func (wh *WebhookHandler) validateTLSSecurityProfiles(hc *hcov1beta1.HyperConverged) error {
1✔
450
        tlsSP := hc.Spec.TLSSecurityProfile
1✔
451

1✔
452
        if tlsSP == nil || tlsSP.Custom == nil {
2✔
453
                return nil
1✔
454
        }
1✔
455

456
        if !isValidTLSProtocolVersion(tlsSP.Custom.MinTLSVersion) {
2✔
457
                return fmt.Errorf("invalid value for spec.tlsSecurityProfile.custom.minTLSVersion")
1✔
458
        }
1✔
459

460
        if tlsSP.Custom.MinTLSVersion < openshiftconfigv1.VersionTLS13 && !hasRequiredHTTP2Ciphers(tlsSP.Custom.Ciphers) {
2✔
461
                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✔
462
        } else if tlsSP.Custom.MinTLSVersion == openshiftconfigv1.VersionTLS13 && len(tlsSP.Custom.Ciphers) > 0 {
3✔
463
                return fmt.Errorf("custom ciphers cannot be selected when minTLSVersion is VersionTLS13")
1✔
464
        }
1✔
465

466
        return nil
1✔
467
}
468

469
func (wh *WebhookHandler) validateTuningPolicy(hc *hcov1beta1.HyperConverged) error {
1✔
470
        if hc.Spec.TuningPolicy == hcov1beta1.HyperConvergedHighBurstProfile { //nolint SA1019
2✔
471
                return newValidationWarning([]string{"spec.tuningPolicy: the highBurst profile is deprecated as of v1.16.0 and will be removed in a future release"})
1✔
472
        }
1✔
473
        return nil
1✔
474
}
475

476
func (wh *WebhookHandler) validateAffinity(hc *hcov1beta1.HyperConverged) error {
1✔
477
        if hc.Spec.Workloads.NodePlacement != nil {
2✔
478
                if err := validateAffinity(hc.Spec.Workloads.NodePlacement.Affinity); err != nil {
2✔
479
                        return fmt.Errorf("invalid workloads node placement affinity: %v", err.Error())
1✔
480
                }
1✔
481
        }
482

483
        if hc.Spec.Infra.NodePlacement != nil {
2✔
484
                if err := validateAffinity(hc.Spec.Infra.NodePlacement.Affinity); err != nil {
2✔
485
                        return fmt.Errorf("invalid infra node placement affinity: %v", err.Error())
1✔
486
                }
1✔
487
        }
488

489
        return nil
1✔
490
}
491

492
func validateAffinity(affinity *corev1.Affinity) error {
1✔
493
        if affinity == nil || affinity.NodeAffinity == nil || affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
2✔
494
                return nil
1✔
495
        }
1✔
496

497
        _, err := nodeaffinity.NewNodeSelector(affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution)
1✔
498

1✔
499
        return err
1✔
500
}
501

502
func hasRequiredHTTP2Ciphers(ciphers []string) bool {
1✔
503
        var requiredHTTP2Ciphers = []string{
1✔
504
                "ECDHE-RSA-AES128-GCM-SHA256",
1✔
505
                "ECDHE-ECDSA-AES128-GCM-SHA256",
1✔
506
        }
1✔
507

1✔
508
        // lo.Some returns true if at least 1 element of a subset is contained into a collection
1✔
509
        return lo.Some[string](requiredHTTP2Ciphers, ciphers)
1✔
510
}
1✔
511

512
// validationResponseFromStatus returns a response for admitting a request with provided Status object.
513
func validationResponseFromStatus(allowed bool, status metav1.Status) admission.Response {
×
514
        resp := admission.Response{
×
515
                AdmissionResponse: admissionv1.AdmissionResponse{
×
516
                        Allowed: allowed,
×
517
                        Result:  &status,
×
518
                },
×
519
        }
×
520
        return resp
×
521
}
×
522

523
func SelectCipherSuitesAndMinTLSVersion() ([]string, openshiftconfigv1.TLSProtocolVersion) {
1✔
524
        ci := hcoutil.GetClusterInfo()
1✔
525
        profile := ci.GetTLSSecurityProfile(hcoTLSConfigCache)
1✔
526

1✔
527
        if profile.Custom != nil {
1✔
528
                return profile.Custom.Ciphers, profile.Custom.MinTLSVersion
×
529
        }
×
530

531
        return openshiftconfigv1.TLSProfiles[profile.Type].Ciphers, openshiftconfigv1.TLSProfiles[profile.Type].MinTLSVersion
1✔
532
}
533

534
func isValidTLSProtocolVersion(pv openshiftconfigv1.TLSProtocolVersion) bool {
1✔
535
        switch pv {
1✔
536
        case
537
                openshiftconfigv1.VersionTLS10,
538
                openshiftconfigv1.VersionTLS11,
539
                openshiftconfigv1.VersionTLS12,
540
                openshiftconfigv1.VersionTLS13:
1✔
541
                return true
1✔
542
        }
543
        return false
1✔
544
}
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