Skip to content

Commit 54e9b56

Browse files
committed
pkg/webhook/admission: upgrade v1beta1 admission types to v1
pkg/builder/webhook_test.go: test both v1 and v1beta1 AdmissionReviews
1 parent 2879800 commit 54e9b56

File tree

12 files changed

+354
-293
lines changed

12 files changed

+354
-293
lines changed

pkg/builder/webhook_test.go

Lines changed: 217 additions & 202 deletions
Large diffs are not rendered by default.

pkg/webhook/admission/decode_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
. "github.com/onsi/ginkgo"
2121
. "github.com/onsi/gomega"
2222

23-
admissionv1beta1 "k8s.io/api/admission/v1beta1"
23+
admissionv1 "k8s.io/api/admission/v1"
2424
corev1 "k8s.io/api/core/v1"
2525
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2626
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -39,7 +39,7 @@ var _ = Describe("Admission Webhook Decoder", func() {
3939
})
4040

4141
req := Request{
42-
AdmissionRequest: admissionv1beta1.AdmissionRequest{
42+
AdmissionRequest: admissionv1.AdmissionRequest{
4343
Object: runtime.RawExtension{
4444
Raw: []byte(`{
4545
"apiVersion": "v1",

pkg/webhook/admission/http.go

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ import (
2424
"io/ioutil"
2525
"net/http"
2626

27+
v1 "k8s.io/api/admission/v1"
2728
"k8s.io/api/admission/v1beta1"
28-
admissionv1beta1 "k8s.io/api/admission/v1beta1"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2930
"k8s.io/apimachinery/pkg/runtime"
31+
"k8s.io/apimachinery/pkg/runtime/schema"
3032
"k8s.io/apimachinery/pkg/runtime/serializer"
3133
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
3234
)
@@ -35,7 +37,8 @@ var admissionScheme = runtime.NewScheme()
3537
var admissionCodecs = serializer.NewCodecFactory(admissionScheme)
3638

3739
func init() {
38-
utilruntime.Must(admissionv1beta1.AddToScheme(admissionScheme))
40+
utilruntime.Must(v1.AddToScheme(admissionScheme))
41+
utilruntime.Must(v1beta1.AddToScheme(admissionScheme))
3942
}
4043

4144
var _ http.Handler = &Webhook{}
@@ -70,11 +73,32 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
7073
return
7174
}
7275

73-
req := Request{}
74-
ar := v1beta1.AdmissionReview{
75-
// avoid an extra copy
76-
Request: &req.AdmissionRequest,
76+
// Both v1 and v1beta1 AdmissionReview types are exactly the same, so the v1beta1 type can
77+
// be decoded into the v1 type. However the runtime codec's decoder guesses which type to
78+
// decode into by GVK if an Object's TypeMeta isn't set. By checking TypeMeta for v1beta1
79+
// and setting API version to v1, the decoder will coerce a v1beta1 AdmissionReview to v1.
80+
ar := unversionedAdmissionReview{}
81+
if err := json.Unmarshal(body, &ar.TypeMeta); err != nil {
82+
wh.log.Error(err, "unable to decode request typemeta")
83+
reviewResponse = Errored(http.StatusBadRequest, err)
84+
wh.writeResponse(w, reviewResponse)
85+
return
7786
}
87+
switch ar.GroupVersionKind() {
88+
case v1.SchemeGroupVersion.WithKind("AdmissionReview"):
89+
case schema.GroupVersionKind{}, v1beta1.SchemeGroupVersion.WithKind("AdmissionReview"):
90+
ar.SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("AdmissionReview"))
91+
default:
92+
err = errors.New("admission review has bad typemeta")
93+
wh.log.Error(err, ar.GroupVersionKind().String())
94+
reviewResponse = Errored(http.StatusBadRequest, err)
95+
wh.writeResponse(w, reviewResponse)
96+
return
97+
}
98+
99+
req := Request{}
100+
// avoid an extra copy
101+
ar.Request = &req.AdmissionRequest
78102
if _, _, err := admissionCodecs.UniversalDeserializer().Decode(body, nil, &ar); err != nil {
79103
wh.log.Error(err, "unable to decode the request")
80104
reviewResponse = Errored(http.StatusBadRequest, err)
@@ -90,7 +114,7 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
90114

91115
func (wh *Webhook) writeResponse(w io.Writer, response Response) {
92116
encoder := json.NewEncoder(w)
93-
responseAdmissionReview := v1beta1.AdmissionReview{
117+
responseAdmissionReview := v1.AdmissionReview{
94118
Response: &response.AdmissionResponse,
95119
}
96120
err := encoder.Encode(responseAdmissionReview)
@@ -107,3 +131,26 @@ func (wh *Webhook) writeResponse(w io.Writer, response Response) {
107131
}
108132
}
109133
}
134+
135+
// unversionedAdmissionReview is used to decode both v1 and v1beta1 AdmissionReview types.
136+
type unversionedAdmissionReview struct {
137+
metav1.TypeMeta `json:",inline"`
138+
Request *v1.AdmissionRequest `json:"request,omitempty"`
139+
Response *v1.AdmissionResponse `json:"response,omitempty"`
140+
}
141+
142+
var _ runtime.Object = &unversionedAdmissionReview{}
143+
144+
func (o unversionedAdmissionReview) DeepCopyObject() runtime.Object {
145+
ar := v1.AdmissionReview{
146+
TypeMeta: o.TypeMeta,
147+
Request: o.Request,
148+
Response: o.Response,
149+
}
150+
aro := ar.DeepCopyObject().(*v1.AdmissionReview)
151+
return &unversionedAdmissionReview{
152+
TypeMeta: aro.TypeMeta,
153+
Request: aro.Request,
154+
Response: aro.Response,
155+
}
156+
}

pkg/webhook/admission/http_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ import (
2727
. "github.com/onsi/ginkgo"
2828
. "github.com/onsi/gomega"
2929

30-
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
30+
admissionv1 "k8s.io/api/admission/v1"
3131

32-
admissionv1beta1 "k8s.io/api/admission/v1beta1"
3332
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
33+
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
3434
)
3535

3636
var _ = Describe("Admission Webhooks", func() {
@@ -76,7 +76,7 @@ var _ = Describe("Admission Webhooks", func() {
7676
}
7777

7878
expected := []byte(
79-
`{"response":{"uid":"","allowed":false,"status":{"metadata":{},"message":"couldn't get version/kind; json parse error: unexpected end of JSON input","code":400}}}
79+
`{"response":{"uid":"","allowed":false,"status":{"metadata":{},"message":"unexpected end of JSON input","code":400}}}
8080
`)
8181
webhook.ServeHTTP(respRecorder, req)
8282
Expect(respRecorder.Body.Bytes()).To(Equal(expected))
@@ -155,7 +155,7 @@ func (h *fakeHandler) Handle(ctx context.Context, req Request) Response {
155155
if h.fn != nil {
156156
return h.fn(ctx, req)
157157
}
158-
return Response{AdmissionResponse: admissionv1beta1.AdmissionResponse{
158+
return Response{AdmissionResponse: admissionv1.AdmissionResponse{
159159
Allowed: true,
160160
}}
161161
}

pkg/webhook/admission/multi.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ import (
2222
"fmt"
2323
"net/http"
2424

25-
"gomodules.xyz/jsonpatch/v2"
26-
admissionv1beta1 "k8s.io/api/admission/v1beta1"
25+
jsonpatch "gomodules.xyz/jsonpatch/v2"
26+
admissionv1 "k8s.io/api/admission/v1"
2727
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
2829
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
2930
)
3031

@@ -37,10 +38,10 @@ func (hs multiMutating) Handle(ctx context.Context, req Request) Response {
3738
if !resp.Allowed {
3839
return resp
3940
}
40-
if resp.PatchType != nil && *resp.PatchType != admissionv1beta1.PatchTypeJSONPatch {
41+
if resp.PatchType != nil && *resp.PatchType != admissionv1.PatchTypeJSONPatch {
4142
return Errored(http.StatusInternalServerError,
4243
fmt.Errorf("unexpected patch type returned by the handler: %v, only allow: %v",
43-
resp.PatchType, admissionv1beta1.PatchTypeJSONPatch))
44+
resp.PatchType, admissionv1.PatchTypeJSONPatch))
4445
}
4546
patches = append(patches, resp.Patches...)
4647
}
@@ -50,13 +51,13 @@ func (hs multiMutating) Handle(ctx context.Context, req Request) Response {
5051
return Errored(http.StatusBadRequest, fmt.Errorf("error when marshaling the patch: %w", err))
5152
}
5253
return Response{
53-
AdmissionResponse: admissionv1beta1.AdmissionResponse{
54+
AdmissionResponse: admissionv1.AdmissionResponse{
5455
Allowed: true,
5556
Result: &metav1.Status{
5657
Code: http.StatusOK,
5758
},
5859
Patch: marshaledPatch,
59-
PatchType: func() *admissionv1beta1.PatchType { pt := admissionv1beta1.PatchTypeJSONPatch; return &pt }(),
60+
PatchType: func() *admissionv1.PatchType { pt := admissionv1.PatchTypeJSONPatch; return &pt }(),
6061
},
6162
}
6263
}
@@ -94,7 +95,7 @@ func (hs multiValidating) Handle(ctx context.Context, req Request) Response {
9495
}
9596
}
9697
return Response{
97-
AdmissionResponse: admissionv1beta1.AdmissionResponse{
98+
AdmissionResponse: admissionv1.AdmissionResponse{
9899
Allowed: true,
99100
Result: &metav1.Status{
100101
Code: http.StatusOK,

pkg/webhook/admission/multi_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ import (
2222
. "github.com/onsi/ginkgo"
2323
. "github.com/onsi/gomega"
2424

25-
"gomodules.xyz/jsonpatch/v2"
26-
admissionv1beta1 "k8s.io/api/admission/v1beta1"
25+
jsonpatch "gomodules.xyz/jsonpatch/v2"
26+
admissionv1 "k8s.io/api/admission/v1"
2727
)
2828

2929
var _ = Describe("Multi-Handler Admission Webhooks", func() {
3030
alwaysAllow := &fakeHandler{
3131
fn: func(ctx context.Context, req Request) Response {
3232
return Response{
33-
AdmissionResponse: admissionv1beta1.AdmissionResponse{
33+
AdmissionResponse: admissionv1.AdmissionResponse{
3434
Allowed: true,
3535
},
3636
}
@@ -39,7 +39,7 @@ var _ = Describe("Multi-Handler Admission Webhooks", func() {
3939
alwaysDeny := &fakeHandler{
4040
fn: func(ctx context.Context, req Request) Response {
4141
return Response{
42-
AdmissionResponse: admissionv1beta1.AdmissionResponse{
42+
AdmissionResponse: admissionv1.AdmissionResponse{
4343
Allowed: false,
4444
},
4545
}
@@ -82,9 +82,9 @@ var _ = Describe("Multi-Handler Admission Webhooks", func() {
8282
Value: "2",
8383
},
8484
},
85-
AdmissionResponse: admissionv1beta1.AdmissionResponse{
85+
AdmissionResponse: admissionv1.AdmissionResponse{
8686
Allowed: true,
87-
PatchType: func() *admissionv1beta1.PatchType { pt := admissionv1beta1.PatchTypeJSONPatch; return &pt }(),
87+
PatchType: func() *admissionv1.PatchType { pt := admissionv1.PatchTypeJSONPatch; return &pt }(),
8888
},
8989
}
9090
},
@@ -99,9 +99,9 @@ var _ = Describe("Multi-Handler Admission Webhooks", func() {
9999
Value: "world",
100100
},
101101
},
102-
AdmissionResponse: admissionv1beta1.AdmissionResponse{
102+
AdmissionResponse: admissionv1.AdmissionResponse{
103103
Allowed: true,
104-
PatchType: func() *admissionv1beta1.PatchType { pt := admissionv1beta1.PatchTypeJSONPatch; return &pt }(),
104+
PatchType: func() *admissionv1.PatchType { pt := admissionv1.PatchTypeJSONPatch; return &pt }(),
105105
},
106106
}
107107
},

pkg/webhook/admission/response.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@ package admission
1919
import (
2020
"net/http"
2121

22-
"gomodules.xyz/jsonpatch/v2"
23-
24-
admissionv1beta1 "k8s.io/api/admission/v1beta1"
22+
jsonpatch "gomodules.xyz/jsonpatch/v2"
23+
admissionv1 "k8s.io/api/admission/v1"
2524
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2625
)
2726

@@ -50,7 +49,7 @@ func Patched(reason string, patches ...jsonpatch.JsonPatchOperation) Response {
5049
// Errored creates a new Response for error-handling a request.
5150
func Errored(code int32, err error) Response {
5251
return Response{
53-
AdmissionResponse: admissionv1beta1.AdmissionResponse{
52+
AdmissionResponse: admissionv1.AdmissionResponse{
5453
Allowed: false,
5554
Result: &metav1.Status{
5655
Code: code,
@@ -67,7 +66,7 @@ func ValidationResponse(allowed bool, reason string) Response {
6766
code = http.StatusOK
6867
}
6968
resp := Response{
70-
AdmissionResponse: admissionv1beta1.AdmissionResponse{
69+
AdmissionResponse: admissionv1.AdmissionResponse{
7170
Allowed: allowed,
7271
Result: &metav1.Status{
7372
Code: int32(code),
@@ -90,17 +89,17 @@ func PatchResponseFromRaw(original, current []byte) Response {
9089
}
9190
return Response{
9291
Patches: patches,
93-
AdmissionResponse: admissionv1beta1.AdmissionResponse{
92+
AdmissionResponse: admissionv1.AdmissionResponse{
9493
Allowed: true,
95-
PatchType: func() *admissionv1beta1.PatchType { pt := admissionv1beta1.PatchTypeJSONPatch; return &pt }(),
94+
PatchType: func() *admissionv1.PatchType { pt := admissionv1.PatchTypeJSONPatch; return &pt }(),
9695
},
9796
}
9897
}
9998

10099
// validationResponseFromStatus returns a response for admitting a request with provided Status object.
101100
func validationResponseFromStatus(allowed bool, status metav1.Status) Response {
102101
resp := Response{
103-
AdmissionResponse: admissionv1beta1.AdmissionResponse{
102+
AdmissionResponse: admissionv1.AdmissionResponse{
104103
Allowed: allowed,
105104
Result: &status,
106105
},

0 commit comments

Comments
 (0)