Skip to content

Commit 6949f07

Browse files
fix(admission): use Ignore failure policy in tests (same as charts) (#5063) (#5078)
(cherry picked from commit a364f35) Co-authored-by: Grzegorz Burzyński <czeslavo@gmail.com>
1 parent 1ed87c1 commit 6949f07

File tree

9 files changed

+62
-62
lines changed

9 files changed

+62
-62
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ Nothing yet.
142142
They must be migrated to annotations. `upstream` field is deprecated - it's recommended
143143
to migrate its settings to the new `KongUpstreamPolicy` resource.
144144
[#5022](https://github.com/Kong/kubernetes-ingress-controller/pull/5022)
145+
- Fixed `HTTPRoute` and `KongConsumer` admission webhook validators to properly
146+
signal validation failures, resulting in returning responses with `AdmissionResponse`
147+
filled instead of 500 status codes. It will make them work as expected in cases where
148+
the `ValidatingWebhookConfiguration` has `failurePolicy: Ignore`.
149+
This will enable validations of `HTTPRoute` and `KongConsumer` that were previously only
150+
accidentally effective with `failurePolicy: Fail`, thus it can be considered a breaking change.
151+
[#5063](https://github.com/Kong/kubernetes-ingress-controller/pull/5063)
145152

146153
### Fixed
147154

internal/admission/handler.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -251,10 +251,7 @@ func (h RequestHandler) handleSecret(
251251

252252
switch request.Operation {
253253
case admissionv1.Update, admissionv1.Create:
254-
ok, message, err := h.Validator.ValidateCredential(ctx, secret)
255-
if err != nil {
256-
return nil, err
257-
}
254+
ok, message := h.Validator.ValidateCredential(ctx, secret)
258255
return responseBuilder.Allowed(ok).WithMessage(message).Build(), nil
259256
default:
260257
return nil, fmt.Errorf("unknown operation %q", string(request.Operation))
@@ -293,7 +290,6 @@ func (h RequestHandler) handleHTTPRoute(
293290
if err != nil {
294291
return nil, err
295292
}
296-
297293
return responseBuilder.Allowed(ok).WithMessage(message).Build(), nil
298294
}
299295

internal/admission/server_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ func (v KongFakeValidator) ValidateClusterPlugin(
5959
return v.Result, v.Message, v.Error
6060
}
6161

62-
func (v KongFakeValidator) ValidateCredential(_ context.Context, _ corev1.Secret) (bool, string, error) {
63-
return v.Result, v.Message, v.Error
62+
func (v KongFakeValidator) ValidateCredential(context.Context, corev1.Secret) (bool, string) {
63+
return v.Result, v.Message
6464
}
6565

6666
func (v KongFakeValidator) ValidateGateway(_ context.Context, _ gatewayapi.Gateway) (bool, string, error) {

internal/admission/validation/gateway/httproute.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func ValidateHTTPRoute(
3434
) (bool, string, error) {
3535
// validate that no unsupported features are in use
3636
if err := validateHTTPRouteFeatures(httproute, parserFeatures); err != nil {
37-
return false, "HTTPRoute spec did not pass validation", err
37+
return false, fmt.Sprintf("HTTPRoute spec did not pass validation: %s", err), nil
3838
}
3939

4040
// perform Gateway validations for the HTTPRoute (e.g. listener validation, namespace validation, e.t.c.)
@@ -45,24 +45,25 @@ func ValidateHTTPRoute(
4545
// determine the parentRef for this gateway
4646
parentRef, err := getParentRefForHTTPRouteGateway(httproute, gateway)
4747
if err != nil {
48-
return false, "Couldn't determine parentRefs for httproute", err
48+
return false, fmt.Sprintf("Couldn't determine parentRefs for httproute: %s", err), nil
4949
}
5050

5151
// gather the relevant gateway listeners for the httproute
5252
listeners, err := getListenersForHTTPRouteValidation(parentRef.SectionName, gateway)
5353
if err != nil {
54-
return false, "Couldn't find gateway listeners for httproute", err
54+
return false, fmt.Sprintf("Couldn't find gateway listeners for httproute: %s", err), nil
5555
}
5656

5757
// perform validation of this route against it's linked gateway listeners
5858
for _, listener := range listeners {
5959
if err := validateHTTPRouteListener(listener); err != nil {
60-
return false, "HTTPRoute linked Gateway listeners did not pass validation", err
60+
return false, fmt.Sprintf("HTTPRoute linked Gateway listeners did not pass validation: %s", err), nil
6161
}
6262
}
6363
}
6464

65-
return validateWithKongGateway(ctx, routesValidator, parserFeatures, httproute)
65+
ok, msg := validateWithKongGateway(ctx, routesValidator, parserFeatures, httproute)
66+
return ok, msg, nil
6667
}
6768

6869
// -----------------------------------------------------------------------------
@@ -188,7 +189,7 @@ func getListenersForHTTPRouteValidation(sectionName *gatewayapi.SectionName, gat
188189

189190
func validateWithKongGateway(
190191
ctx context.Context, routesValidator routeValidator, parserFeatures parser.FeatureFlags, httproute *gatewayapi.HTTPRoute,
191-
) (bool, string, error) {
192+
) (bool, string) {
192193
// Translate HTTPRoute to Kong Route object(s) that can be sent directly to the Admin API for validation.
193194
// Use KIC parser that works both for traditional and expressions based routes.
194195
var kongRoutes []kong.Route
@@ -211,23 +212,23 @@ func validateWithKongGateway(
211212
}
212213
}
213214
if len(errMsgs) > 0 {
214-
return false, validationMsg(errMsgs), nil
215+
return false, validationMsg(errMsgs)
215216
}
216217
// Validate by using feature of Kong Gateway.
217218
for _, kg := range kongRoutes {
218219
kg := kg
219220
ok, msg, err := routesValidator.Validate(ctx, &kg)
220221
if err != nil {
221-
return false, fmt.Sprintf("Unable to validate HTTPRoute schema: %s", err.Error()), nil
222+
return false, fmt.Sprintf("Unable to validate HTTPRoute schema: %s", err.Error())
222223
}
223224
if !ok {
224225
errMsgs = append(errMsgs, msg)
225226
}
226227
}
227228
if len(errMsgs) > 0 {
228-
return false, validationMsg(errMsgs), nil
229+
return false, validationMsg(errMsgs)
229230
}
230-
return true, "", nil
231+
return true, ""
231232
}
232233

233234
func validationMsg(errMsgs []string) string {

internal/admission/validation/gateway/httproute_test.go

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package gateway
22

33
import (
44
"context"
5-
"fmt"
65
"testing"
76

87
"github.com/kong/go-kong/kong"
@@ -59,8 +58,7 @@ func TestValidateHTTPRoute(t *testing.T) {
5958
},
6059
}},
6160
valid: false,
62-
validationMsg: "Couldn't determine parentRefs for httproute",
63-
err: fmt.Errorf("no parentRef matched gateway default/testing-gateway"),
61+
validationMsg: "Couldn't determine parentRefs for httproute: no parentRef matched gateway default/testing-gateway",
6462
},
6563
{
6664
msg: "if you use sectionname to attach to a non-existent gateway listener, it fails validation",
@@ -98,8 +96,7 @@ func TestValidateHTTPRoute(t *testing.T) {
9896
},
9997
}},
10098
valid: false,
101-
validationMsg: "Couldn't find gateway listeners for httproute",
102-
err: fmt.Errorf("sectionname referenced listener listener-that-doesnt-exist was not found on gateway default/testing-gateway"),
99+
validationMsg: "Couldn't find gateway listeners for httproute: sectionname referenced listener listener-that-doesnt-exist was not found on gateway default/testing-gateway",
103100
},
104101
{
105102
msg: "if the provided gateway has NO listeners, the HTTPRoute fails validation",
@@ -126,8 +123,7 @@ func TestValidateHTTPRoute(t *testing.T) {
126123
},
127124
}},
128125
valid: false,
129-
validationMsg: "Couldn't find gateway listeners for httproute",
130-
err: fmt.Errorf("no listeners could be found for gateway default/testing-gateway"),
126+
validationMsg: "Couldn't find gateway listeners for httproute: no listeners could be found for gateway default/testing-gateway",
131127
},
132128
{
133129
msg: "parentRefs which omit the namespace pass validation in the same namespace",
@@ -200,8 +196,7 @@ func TestValidateHTTPRoute(t *testing.T) {
200196
},
201197
}},
202198
valid: false,
203-
validationMsg: "HTTPRoute linked Gateway listeners did not pass validation",
204-
err: fmt.Errorf("HTTPRoute not supported by listener http-alternate"),
199+
validationMsg: "HTTPRoute linked Gateway listeners did not pass validation: HTTPRoute not supported by listener http-alternate",
205200
},
206201
{
207202
msg: "if an HTTPRoute is using queryparams matching it fails validation due to only supporting expression router",
@@ -253,8 +248,7 @@ func TestValidateHTTPRoute(t *testing.T) {
253248
},
254249
}},
255250
valid: false,
256-
validationMsg: "HTTPRoute spec did not pass validation",
257-
err: fmt.Errorf("queryparam matching is supported with expression router only"),
251+
validationMsg: "HTTPRoute spec did not pass validation: queryparam matching is supported with expression router only",
258252
},
259253
{
260254
msg: "we don't support any group except core kubernetes for backendRefs",
@@ -311,8 +305,7 @@ func TestValidateHTTPRoute(t *testing.T) {
311305
},
312306
}},
313307
valid: false,
314-
validationMsg: "HTTPRoute spec did not pass validation",
315-
err: fmt.Errorf("example is not a supported group for httproute backendRefs, only core is supported"),
308+
validationMsg: "HTTPRoute spec did not pass validation: example is not a supported group for httproute backendRefs, only core is supported",
316309
},
317310
{
318311
msg: "we don't support any core kind except Service for backendRefs",
@@ -368,17 +361,18 @@ func TestValidateHTTPRoute(t *testing.T) {
368361
},
369362
}},
370363
valid: false,
371-
validationMsg: "HTTPRoute spec did not pass validation",
372-
err: fmt.Errorf("Pod is not a supported kind for httproute backendRefs, only Service is supported"),
364+
validationMsg: "HTTPRoute spec did not pass validation: Pod is not a supported kind for httproute backendRefs, only Service is supported",
373365
},
374366
} {
375-
// Passed routesValidator is irrelevant for the above test cases.
376-
valid, validMsg, err := ValidateHTTPRoute(
377-
context.Background(), mockRoutesValidator{}, parser.FeatureFlags{}, tt.route, tt.gateways...,
378-
)
379-
assert.Equal(t, tt.valid, valid, tt.msg)
380-
assert.Equal(t, tt.validationMsg, validMsg, tt.msg)
381-
assert.Equal(t, tt.err, err, tt.msg)
367+
t.Run(tt.msg, func(t *testing.T) {
368+
// Passed routesValidator is irrelevant for the above test cases.
369+
valid, validMsg, err := ValidateHTTPRoute(
370+
context.Background(), mockRoutesValidator{}, parser.FeatureFlags{}, tt.route, tt.gateways...,
371+
)
372+
assert.Equal(t, tt.valid, valid, tt.msg)
373+
assert.Equal(t, tt.validationMsg, validMsg, tt.msg)
374+
assert.Equal(t, tt.err, err, tt.msg)
375+
})
382376
}
383377
}
384378

internal/admission/validator.go

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ type KongValidator interface {
3232
ValidateConsumerGroup(ctx context.Context, consumerGroup kongv1beta1.KongConsumerGroup) (bool, string, error)
3333
ValidatePlugin(ctx context.Context, plugin kongv1.KongPlugin) (bool, string, error)
3434
ValidateClusterPlugin(ctx context.Context, plugin kongv1.KongClusterPlugin) (bool, string, error)
35-
ValidateCredential(ctx context.Context, secret corev1.Secret) (bool, string, error)
35+
ValidateCredential(ctx context.Context, secret corev1.Secret) (bool, string)
3636
ValidateGateway(ctx context.Context, gateway gatewayapi.Gateway) (bool, string, error)
3737
ValidateHTTPRoute(ctx context.Context, httproute gatewayapi.HTTPRoute) (bool, string, error)
3838
ValidateIngress(ctx context.Context, ingress netv1.Ingress) (bool, string, error)
@@ -125,7 +125,7 @@ func (validator KongHTTPValidator) ValidateConsumer(
125125
// credentials so that the consumers credentials references can be validated.
126126
managedConsumers, err := validator.listManagedConsumers(ctx)
127127
if err != nil {
128-
return false, ErrTextConsumerUnretrievable, err
128+
return false, fmt.Sprintf("failed to fetch managed KongConsumers from cache: %s", err), nil
129129
}
130130

131131
// retrieve the consumer's credentials secrets to validate them with the index
@@ -136,14 +136,14 @@ func (validator KongHTTPValidator) ValidateConsumer(
136136
secret, err := validator.SecretGetter.GetSecret(consumer.Namespace, secretName)
137137
if err != nil {
138138
if apierrors.IsNotFound(err) {
139-
return false, ErrTextConsumerCredentialSecretNotFound, err
139+
return false, fmt.Sprintf("%s: %s", ErrTextConsumerCredentialSecretNotFound, err), nil
140140
}
141141
return false, ErrTextFailedToRetrieveSecret, err
142142
}
143143

144144
// do the basic credentials validation
145145
if err := credsvalidation.ValidateCredentials(secret); err != nil {
146-
return false, ErrTextConsumerCredentialValidationFailed, err
146+
return false, fmt.Sprintf("%s: %s", ErrTextConsumerCredentialValidationFailed, err), nil
147147
}
148148

149149
// if valid, store it so we can index it for upcoming constraints validation
@@ -164,15 +164,15 @@ func (validator KongHTTPValidator) ValidateConsumer(
164164
// testing them against themselves.
165165
credentialsIndex, err := globalValidationIndexForCredentials(ctx, validator.ManagerClient, managedConsumers, ignoredSecrets)
166166
if err != nil {
167-
return false, ErrTextConsumerCredentialValidationFailed, err
167+
return false, fmt.Sprintf("%s: %s", ErrTextConsumerCredentialValidationFailed, err), nil
168168
}
169169

170170
// validate the consumer's credentials against the index of all managed
171171
// credentials to ensure they're not in violation of any unique constraints.
172172
for _, secret := range credentials {
173173
// do the unique constraints validation of the credentials using the credentials index
174174
if err := credentialsIndex.ValidateCredentialsForUniqueKeyConstraints(secret); err != nil {
175-
return false, ErrTextConsumerCredentialValidationFailed, err
175+
return false, fmt.Sprintf("%s: %s", ErrTextConsumerCredentialValidationFailed, err), nil
176176
}
177177
}
178178

@@ -232,34 +232,31 @@ func (validator KongHTTPValidator) ValidateConsumerGroup(
232232
// are present in it or not. If valid, it returns true with an empty string,
233233
// else it returns false with the error message. If an error happens during
234234
// validation, error is returned.
235-
func (validator KongHTTPValidator) ValidateCredential(
236-
ctx context.Context,
237-
secret corev1.Secret,
238-
) (bool, string, error) {
235+
func (validator KongHTTPValidator) ValidateCredential(ctx context.Context, secret corev1.Secret) (bool, string) {
239236
// If the secret doesn't specify a credential type (either by label or the secret's key) it's not a credentials secret.
240237
if _, s := util.ExtractKongCredentialType(&secret); s == util.CredentialTypeAbsent {
241-
return true, "", nil
238+
return true, ""
242239
}
243240

244241
// If we know it's a credentials secret, we can ensure its base-level validity.
245242
if err := credsvalidation.ValidateCredentials(&secret); err != nil {
246-
return false, fmt.Sprintf("%s: %s", ErrTextConsumerCredentialValidationFailed, err), nil
243+
return false, fmt.Sprintf("%s: %s", ErrTextConsumerCredentialValidationFailed, err)
247244
}
248245

249246
// Credentials are validated further for unique key constraints only if they are referenced by a managed consumer
250247
// in the namespace, as such we pull a list of all consumers from the cached client to determine
251248
// if the credentials are referenced.
252249
managedConsumers, err := validator.listManagedConsumers(ctx)
253250
if err != nil {
254-
return false, ErrTextConsumerUnretrievable, err
251+
return false, fmt.Sprintf("failed to fetch managed KongConsumers from cache: %s", err)
255252
}
256253

257254
// Verify whether this secret is referenced by any managed consumer.
258255
managedConsumersWithReferences := listManagedConsumersReferencingCredentialsSecret(secret, managedConsumers)
259256
if len(managedConsumersWithReferences) == 0 {
260257
// If no managed consumers reference this secret, its considered unmanaged, and we don't validate it
261258
// unless it becomes referenced by a managed consumer at a later time.
262-
return true, "", nil
259+
return true, ""
263260
}
264261

265262
// If base-level validation passed and the credential is referenced by a consumer,
@@ -268,16 +265,16 @@ func (validator KongHTTPValidator) ValidateCredential(
268265
ignoreSecrets := map[string]map[string]struct{}{secret.Namespace: {secret.Name: {}}}
269266
credentialsIndex, err := globalValidationIndexForCredentials(ctx, validator.ManagerClient, managedConsumers, ignoreSecrets)
270267
if err != nil {
271-
return false, ErrTextConsumerCredentialValidationFailed, err
268+
return false, fmt.Sprintf("%s: %s", ErrTextConsumerCredentialValidationFailed, err)
272269
}
273270

274271
// The index is built, now validate that the newly updated secret
275272
// is not in violation of any constraints.
276273
if err := credentialsIndex.ValidateCredentialsForUniqueKeyConstraints(&secret); err != nil {
277-
return false, fmt.Sprintf("%s: %s", ErrTextConsumerCredentialValidationFailed, err), nil
274+
return false, fmt.Sprintf("%s: %s", ErrTextConsumerCredentialValidationFailed, err)
278275
}
279276

280-
return true, "", nil
277+
return true, ""
281278
}
282279

283280
// ValidatePlugin checks if k8sPlugin is valid. It does so by performing
@@ -404,13 +401,19 @@ func (validator KongHTTPValidator) ValidateHTTPRoute(
404401
Namespace: namespace,
405402
Name: string(parentRef.Name),
406403
}, &gateway); err != nil {
407-
return false, fmt.Sprintf("Couldn't retrieve referenced gateway %s/%s", namespace, parentRef.Name), err
404+
if apierrors.IsNotFound(err) {
405+
return false, fmt.Sprintf("referenced gateway %s/%s not found", namespace, parentRef.Name), nil
406+
}
407+
return false, "", err
408408
}
409409

410410
// pull the referenced GatewayClass object from the Gateway
411411
gatewayClass := gatewayapi.GatewayClass{}
412412
if err := validator.ManagerClient.Get(ctx, client.ObjectKey{Name: string(gateway.Spec.GatewayClassName)}, &gatewayClass); err != nil {
413-
return false, fmt.Sprintf("Couldn't retrieve referenced gatewayclass %s", gateway.Spec.GatewayClassName), err
413+
if apierrors.IsNotFound(err) {
414+
return false, fmt.Sprintf("referenced gatewayclass %s not found", gateway.Spec.GatewayClassName), nil
415+
}
416+
return false, "", err
414417
}
415418

416419
// determine ultimately whether the Gateway is managed by this controller implementation

internal/admission/validator_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -664,8 +664,7 @@ func TestKongHTTPValidator_ValidateCredential(t *testing.T) {
664664
Logger: logr.Discard(),
665665
}
666666

667-
ok, msg, err := validator.ValidateCredential(context.Background(), tc.secret)
668-
require.NoError(t, err)
667+
ok, msg := validator.ValidateCredential(context.Background(), tc.secret)
669668
assert.Equal(t, tc.wantOK, ok)
670669
assert.Equal(t, tc.wantMessage, msg)
671670
})

test/integration/httproute_webhook_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func commonHTTPRouteValidationTestCases(
6262
},
6363
},
6464
},
65-
WantCreateErrSubstring: `Gateway.gateway.networking.k8s.io \"fake-gateway\" not found`,
65+
WantCreateErrSubstring: `fake-gateway not found`,
6666
},
6767
{
6868
Name: "an invalid httproute will pass validation if it's not linked to a managed controller (it's not ours)",

test/integration/webhook_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ func ensureAdmissionRegistration(ctx context.Context, t *testing.T, namespace, c
611611
Webhooks: []admregv1.ValidatingWebhook{
612612
{
613613
Name: "validations.kong.konghq.com",
614-
FailurePolicy: lo.ToPtr(admregv1.Fail),
614+
FailurePolicy: lo.ToPtr(admregv1.Ignore),
615615
SideEffects: lo.ToPtr(admregv1.SideEffectClassNone),
616616
AdmissionReviewVersions: []string{"v1beta1", "v1"},
617617
Rules: rules,

0 commit comments

Comments
 (0)