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

kubevirt / hyperconverged-cluster-operator / 22618055699

03 Mar 2026 10:06AM UTC coverage: 79.031%. First build
22618055699

Pull #4063

github

web-flow
Merge 46a578e49 into c24cc7c74
Pull Request #4063: CNV-78904: Add webhooks for v1 + conversion webhook

555 of 660 new or added lines in 7 files covered. (84.09%)

9294 of 11760 relevant lines covered (79.03%)

1.95 hits per line

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

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

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

10
        "github.com/go-logr/logr"
11
        openshiftconfigv1 "github.com/openshift/api/config/v1"
12
        "github.com/samber/lo"
13
        admissionv1 "k8s.io/api/admission/v1"
14
        corev1 "k8s.io/api/core/v1"
15
        apierrors "k8s.io/apimachinery/pkg/api/errors"
16
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17
        "k8s.io/component-helpers/scheduling/corev1/nodeaffinity"
18
        "sigs.k8s.io/controller-runtime/pkg/client"
19
        "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
20

21
        hcov1 "github.com/kubevirt/hyperconverged-cluster-operator/api/v1"
22
        hcov1beta1 "github.com/kubevirt/hyperconverged-cluster-operator/api/v1beta1"
23
)
24

25
const (
26
        updateDryRunTimeOut = time.Second * 3
27

28
        validatorV1Name = "hyperConverged v1 validator"
29
)
30

31
type ValidationWarning struct {
32
        warnings []string
33
}
34

35
func newValidationWarning(warnings []string) *ValidationWarning {
1✔
36
        return &ValidationWarning{
1✔
37
                warnings: warnings,
1✔
38
        }
1✔
39
}
1✔
40

41
func (v *ValidationWarning) Error() string {
×
42
        return ""
×
43
}
×
44

45
func (v *ValidationWarning) Warnings() []string {
×
46
        return v.warnings
×
47
}
×
48

49
type WebhookHandler struct {
50
        logger         logr.Logger
51
        cli            client.Client
52
        namespace      string
53
        isOpenshift    bool
54
        decoder        admission.Decoder
55
        v1beta1Handler *WebhookV1Beta1Handler
56
}
57

58
func NewWebhookHandler(logger logr.Logger, cli client.Client, decoder admission.Decoder, namespace string, isOpenshift bool, v1beta1Handler *WebhookV1Beta1Handler) *WebhookHandler {
2✔
59
        return &WebhookHandler{
2✔
60
                logger:         logger.WithName(validatorV1Name),
2✔
61
                cli:            cli,
2✔
62
                namespace:      namespace,
2✔
63
                isOpenshift:    isOpenshift,
2✔
64
                decoder:        decoder,
2✔
65
                v1beta1Handler: v1beta1Handler,
2✔
66
        }
2✔
67
}
2✔
68

69
func (wh *WebhookHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
1✔
70
        ctx = admission.NewContextWithRequest(ctx, req)
1✔
71
        logger, err := logr.FromContext(ctx)
1✔
72
        if err != nil {
2✔
73
                logger = wh.logger
1✔
74
        } else {
1✔
NEW
75
                logger = logger.WithName(validatorV1Name)
×
NEW
76
        }
×
77

78
        // Get the object in the request
79
        v1obj := &hcov1.HyperConverged{}
1✔
80

1✔
81
        dryRun := req.DryRun != nil && *req.DryRun
1✔
82

1✔
83
        switch req.Operation {
1✔
84
        case admissionv1.Create:
1✔
85
                if err = wh.decoder.Decode(req, v1obj); err != nil {
2✔
86
                        return admission.Errored(http.StatusBadRequest, err)
1✔
87
                }
1✔
88

89
                obj := &hcov1beta1.HyperConverged{}
1✔
90
                err = obj.ConvertFrom(v1obj)
1✔
91
                if err != nil {
1✔
NEW
92
                        return admission.Errored(http.StatusInternalServerError, err)
×
NEW
93
                }
×
94

95
                err = wh.v1beta1Handler.ValidateCreate(ctx, logger, dryRun, obj)
1✔
96
        case admissionv1.Update:
1✔
97
                v1OldObj := &hcov1.HyperConverged{}
1✔
98
                if err = wh.decoder.DecodeRaw(req.Object, v1obj); err != nil {
2✔
99
                        return admission.Errored(http.StatusBadRequest, err)
1✔
100
                }
1✔
101
                if err = wh.decoder.DecodeRaw(req.OldObject, v1OldObj); err != nil {
2✔
102
                        return admission.Errored(http.StatusBadRequest, err)
1✔
103
                }
1✔
104

105
                obj := &hcov1beta1.HyperConverged{}
1✔
106
                err = obj.ConvertFrom(v1obj)
1✔
107
                if err != nil {
1✔
NEW
108
                        return admission.Errored(http.StatusInternalServerError, err)
×
NEW
109
                }
×
110

111
                oldObj := &hcov1beta1.HyperConverged{}
1✔
112
                err = oldObj.ConvertFrom(v1OldObj)
1✔
113
                if err != nil {
1✔
NEW
114
                        return admission.Errored(http.StatusInternalServerError, err)
×
NEW
115
                }
×
116

117
                err = wh.v1beta1Handler.ValidateUpdate(ctx, logger, dryRun, obj, oldObj)
1✔
118
        case admissionv1.Delete:
1✔
119
                if err = wh.decoder.DecodeRaw(req.OldObject, v1obj); err != nil {
2✔
120
                        return admission.Errored(http.StatusBadRequest, err)
1✔
121
                }
1✔
122

123
                obj := &hcov1beta1.HyperConverged{}
1✔
124
                err = obj.ConvertFrom(v1obj)
1✔
125
                if err != nil {
1✔
NEW
126
                        return admission.Errored(http.StatusInternalServerError, err)
×
NEW
127
                }
×
128

129
                err = wh.v1beta1Handler.ValidateDelete(ctx, logger, dryRun, obj)
1✔
130
        default:
1✔
131
                return admission.Errored(http.StatusBadRequest, fmt.Errorf("unknown operation request %q", req.Operation))
1✔
132
        }
133

134
        // Check the error message first.
135
        if err != nil {
1✔
136
                var apiStatus apierrors.APIStatus
×
137
                if errors.As(err, &apiStatus) {
×
138
                        return validationResponseFromStatus(false, apiStatus.Status())
×
139
                }
×
140

141
                var vw *ValidationWarning
×
142
                if errors.As(err, &vw) {
×
143
                        return admission.Allowed("").WithWarnings(vw.Warnings()...)
×
144
                }
×
145

146
                return admission.Denied(err.Error())
×
147
        }
148

149
        // Return allowed if everything succeeded.
150
        return admission.Allowed("")
1✔
151
}
152

153
func hasRequiredHTTP2Ciphers(ciphers []string) bool {
1✔
154
        var requiredHTTP2Ciphers = []string{
1✔
155
                "ECDHE-RSA-AES128-GCM-SHA256",
1✔
156
                "ECDHE-ECDSA-AES128-GCM-SHA256",
1✔
157
        }
1✔
158

1✔
159
        // lo.Some returns true if at least 1 element of a subset is contained into a collection
1✔
160
        return lo.Some[string](requiredHTTP2Ciphers, ciphers)
1✔
161
}
1✔
162

163
// validationResponseFromStatus returns a response for admitting a request with provided Status object.
164
func validationResponseFromStatus(allowed bool, status metav1.Status) admission.Response {
×
165
        resp := admission.Response{
×
166
                AdmissionResponse: admissionv1.AdmissionResponse{
×
167
                        Allowed: allowed,
×
168
                        Result:  &status,
×
169
                },
×
170
        }
×
171
        return resp
×
172
}
×
173

174
func isValidTLSProtocolVersion(pv openshiftconfigv1.TLSProtocolVersion) bool {
1✔
175
        switch pv {
1✔
176
        case
177
                openshiftconfigv1.VersionTLS10,
178
                openshiftconfigv1.VersionTLS11,
179
                openshiftconfigv1.VersionTLS12,
180
                openshiftconfigv1.VersionTLS13:
1✔
181
                return true
1✔
182
        }
183
        return false
1✔
184
}
185

186
func validateAffinity(affinity *corev1.Affinity) error {
1✔
187
        if affinity == nil || affinity.NodeAffinity == nil || affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
2✔
188
                return nil
1✔
189
        }
1✔
190

191
        _, err := nodeaffinity.NewNodeSelector(affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution)
1✔
192

1✔
193
        return err
1✔
194
}
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