Skip to content

Commit

Permalink
Added Rejected field to MutatorResult, fixes slok#189
Browse files Browse the repository at this point in the history
  • Loading branch information
SOF3 committed Jun 9, 2022
1 parent 7cda665 commit 7155978
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 7 deletions.
15 changes: 13 additions & 2 deletions pkg/http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,15 @@ func (h handler) validatingModelResponseToJSON(ctx context.Context, review model
}

func (h handler) mutatingModelResponseToJSON(ctx context.Context, review model.AdmissionReview, resp *model.MutatingAdmissionResponse) (data []byte, err error) {
var resultStatus *metav1.Status
if resp.Rejected {
resultStatus = &metav1.Status{
Message: resp.Message,
Status: metav1.StatusFailure,
Code: http.StatusBadRequest,
}
}

switch review.OriginalAdmissionReview.(type) {
case *admissionv1beta1.AdmissionReview:
if len(resp.Warnings) > 0 {
Expand All @@ -290,7 +299,8 @@ func (h handler) mutatingModelResponseToJSON(ctx context.Context, review model.A
UID: types.UID(review.ID),
PatchType: v1beta1JSONPatchType,
Patch: resp.JSONPatchPatch,
Allowed: true,
Allowed: !resp.Rejected,
Result: resultStatus,
},
})
return data, err
Expand All @@ -302,8 +312,9 @@ func (h handler) mutatingModelResponseToJSON(ctx context.Context, review model.A
UID: types.UID(review.ID),
PatchType: v1JSONPatchType,
Patch: resp.JSONPatchPatch,
Allowed: true,
Allowed: !resp.Rejected,
Warnings: resp.Warnings,
Result: resultStatus,
},
})

Expand Down
40 changes: 36 additions & 4 deletions pkg/http/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,29 @@ func TestDefaultWebhookFlow(t *testing.T) {
expCode: 200,
},

"A correct rejected mutating admission v1beta1 webhook without mutation should not fail.": {
body: getTestAdmissionReviewV1beta1RequestStr("1234567890"),
mock: func(mw *webhookmock.Webhook) {
resp := &model.MutatingAdmissionResponse{
ID: "1234567890",
Rejected: true,
Message: "this is not valid because reasons",
JSONPatchPatch: []byte(``),
Warnings: []string{"warn1", "warn2"}, // v1beta1 ignores warnings.
}
mw.On("Review", mock.Anything, mock.Anything).Once().Return(resp, nil)
},
expBody: `{"kind":"AdmissionReview","apiVersion":"admission.k8s.io/v1beta1","response":{"uid":"1234567890","allowed":false,"status":{"metadata":{},"status":"Failure","message":"this is not valid because reasons","code":400},"patchType":"JSONPatch"}}`,
expCode: 200,
},

"A correct validation admission v1 webhook that allows should not fail.": {
body: getTestAdmissionReviewV1RequestStr("1234567890"),
mock: func(mw *webhookmock.Webhook) {
resp := &model.ValidatingAdmissionResponse{
ID: "1234567890",
Allowed: true,
Warnings: []string{"warn1", "warn2"}, // v1beta1 ignores warnings.
Warnings: []string{"warn1", "warn2"}, // v1 includes warnings.
}
mw.On("Review", mock.Anything, mock.Anything).Once().Return(resp, nil)
},
Expand All @@ -195,7 +211,7 @@ func TestDefaultWebhookFlow(t *testing.T) {
ID: "1234567890",
Allowed: false,
Message: "this is not valid because reasons",
Warnings: []string{"warn1", "warn2"}, // v1beta1 ignores warnings.
Warnings: []string{"warn1", "warn2"}, // v1 includes warnings.
}
mw.On("Review", mock.Anything, mock.Anything).Once().Return(resp, nil)
},
Expand All @@ -209,7 +225,7 @@ func TestDefaultWebhookFlow(t *testing.T) {
resp := &model.MutatingAdmissionResponse{
ID: "1234567890",
JSONPatchPatch: []byte(`{"something": something}`),
Warnings: []string{"warn1", "warn2"}, // v1beta1 ignores warnings.
Warnings: []string{"warn1", "warn2"}, // v1 includes warnings.
}
mw.On("Review", mock.Anything, mock.Anything).Once().Return(resp, nil)
},
Expand All @@ -223,14 +239,30 @@ func TestDefaultWebhookFlow(t *testing.T) {
resp := &model.MutatingAdmissionResponse{
ID: "1234567890",
JSONPatchPatch: []byte(``),
Warnings: []string{"warn1", "warn2"}, // v1beta1 ignores warnings.
Warnings: []string{"warn1", "warn2"}, // v1 includes warnings.
}
mw.On("Review", mock.Anything, mock.Anything).Once().Return(resp, nil)
},
expBody: `{"kind":"AdmissionReview","apiVersion":"admission.k8s.io/v1","response":{"uid":"1234567890","allowed":true,"patchType":"JSONPatch","warnings":["warn1","warn2"]}}`,
expCode: 200,
},

"A correct rejected mutating admission v1 webhook without mutation should not fail.": {
body: getTestAdmissionReviewV1RequestStr("1234567890"),
mock: func(mw *webhookmock.Webhook) {
resp := &model.MutatingAdmissionResponse{
ID: "1234567890",
Rejected: true,
Message: "this is not valid because reasons",
JSONPatchPatch: []byte(``),
Warnings: []string{"warn1", "warn2"}, // v1 includes warnings.
}
mw.On("Review", mock.Anything, mock.Anything).Once().Return(resp, nil)
},
expBody: `{"kind":"AdmissionReview","apiVersion":"admission.k8s.io/v1","response":{"uid":"1234567890","allowed":false,"status":{"metadata":{},"status":"Failure","message":"this is not valid because reasons","code":400},"patchType":"JSONPatch","warnings":["warn1","warn2"]}}`,
expCode: 200,
},

"A regular mutating admission v1beta1 call to the webhook handler should execute the webhook and return error if something failed": {
body: getTestAdmissionReviewV1beta1RequestStr("1234567890"),
mock: func(mw *webhookmock.Webhook) {
Expand Down
2 changes: 2 additions & 0 deletions pkg/model/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type MutatingAdmissionResponse struct {
admissionResponse

ID string
Rejected bool
Message string
JSONPatchPatch []byte
Warnings []string
}
Expand Down
8 changes: 7 additions & 1 deletion pkg/webhook/mutating/mutator.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ import (
type MutatorResult struct {
// StopChain will stop the chain of validators in case there is a chain set.
StopChain bool
// Reject tells the apiserver that the resource is incorrect and it should reject the request.
// Contrary to ValidatorResult, Reject is used instead of Allowed so that the default value is to allow.
// If Reject is true, this implies StopChain.
Reject bool
// Message will be used by the apiserver to give more information in case the resource is not valid.
Message string
// MutatedObject is the object that has been mutated. If is nil, it will be used the one
// received by the Mutator.
MutatedObject metav1.Object
Expand Down Expand Up @@ -83,7 +89,7 @@ func (c *Chain) Mutate(ctx context.Context, ar *model.AdmissionReview, obj metav
obj = res.MutatedObject
}

if res.StopChain {
if res.StopChain || res.Reject {
res.Warnings = warnings
return res, nil
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/webhook/mutating/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ func (w mutatingWebhook) mutatingAdmissionReview(ctx context.Context, ar model.A
// Forge response.
return &model.MutatingAdmissionResponse{
ID: ar.ID,
Rejected: res.Reject,
Message: res.Message,
JSONPatchPatch: marshalledPatch,
Warnings: res.Warnings,
}, nil
Expand Down

0 comments on commit 7155978

Please sign in to comment.