Skip to content

Commit 107c8e9

Browse files
committed
Fix admission for decoding for unstructured
Due to a weird interaction between the unstructured decoder (which demands APIVersion and Kind fields) and the API server (which fails to set those fields on the embedded object in admission requests), we can't use the normal decoders, nor can we call json.Unmarshal directly on the unstructured (since it implements an unmarshaller that calls back into the decoder). This detects unstructured objects, and does the right thing for them.
1 parent 1859301 commit 107c8e9

File tree

2 files changed

+31
-0
lines changed

2 files changed

+31
-0
lines changed

pkg/webhook/admission/decode.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ limitations under the License.
1717
package admission
1818

1919
import (
20+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2021
"k8s.io/apimachinery/pkg/runtime"
2122
"k8s.io/apimachinery/pkg/runtime/serializer"
23+
"k8s.io/apimachinery/pkg/util/json"
2224
)
2325

2426
// Decoder knows how to decode the contents of an admission
@@ -34,6 +36,22 @@ func NewDecoder(scheme *runtime.Scheme) (*Decoder, error) {
3436

3537
// Decode decodes the inlined object in the AdmissionRequest into the passed-in runtime.Object.
3638
func (d *Decoder) Decode(req Request, into runtime.Object) error {
39+
// NB(directxman12): there's a bug/weird interaction between decoders and
40+
// the API server where the API server doesn't send a GVK on the embedded
41+
// objects, which means the unstructured decoder refuses to decode. It
42+
// also means we can't pass the unstructured directly in, since it'll try
43+
// and call unstructured's special Unmarshal implementation, which calls
44+
// back into that same decoder :-/
45+
// See kubernetes/kubernetes#74373.
46+
if unstructuredInto, isUnstructured := into.(*unstructured.Unstructured); isUnstructured {
47+
// unmarshal into unstructured's underlying object to avoid calling the decoder
48+
if err := json.Unmarshal(req.Object.Raw, &unstructuredInto.Object); err != nil {
49+
return err
50+
}
51+
52+
return nil
53+
}
54+
3755
deserializer := d.codecs.UniversalDeserializer()
3856
return runtime.DecodeInto(deserializer, req.AdmissionRequest.Object.Raw, into)
3957
}

pkg/webhook/admission/decode_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
admissionv1beta1 "k8s.io/api/admission/v1beta1"
2424
corev1 "k8s.io/api/core/v1"
2525
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2627
"k8s.io/apimachinery/pkg/runtime"
2728
"k8s.io/client-go/kubernetes/scheme"
2829
)
@@ -87,4 +88,16 @@ var _ = Describe("Admission Webhook Decoder", func() {
8788
By("trying to extract a pod into a node")
8889
Expect(decoder.Decode(req, &corev1.Node{})).NotTo(Succeed())
8990
})
91+
92+
It("should be able to decode into an unstructured object", func() {
93+
By("decoding into an unstructured object")
94+
var target unstructured.Unstructured
95+
Expect(decoder.Decode(req, &target)).To(Succeed())
96+
97+
By("sanity-checking the metadata on the output object")
98+
Expect(target.Object["metadata"]).To(Equal(map[string]interface{}{
99+
"name": "foo",
100+
"namespace": "default",
101+
}))
102+
})
90103
})

0 commit comments

Comments
 (0)