...

Source file src/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode_test.go

Documentation: sigs.k8s.io/controller-runtime/pkg/webhook/admission

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8     http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package admission
    18  
    19  import (
    20  	. "github.com/onsi/ginkgo/v2"
    21  	. "github.com/onsi/gomega"
    22  
    23  	admissionv1 "k8s.io/api/admission/v1"
    24  	corev1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	"k8s.io/client-go/kubernetes/scheme"
    29  )
    30  
    31  var _ = Describe("Admission Webhook Decoder", func() {
    32  	var decoder Decoder
    33  	BeforeEach(func() {
    34  		By("creating a new decoder for a scheme")
    35  		decoder = NewDecoder(scheme.Scheme)
    36  		Expect(decoder).NotTo(BeNil())
    37  	})
    38  
    39  	req := Request{
    40  		AdmissionRequest: admissionv1.AdmissionRequest{
    41  			Object: runtime.RawExtension{
    42  				Raw: []byte(`{
    43      "apiVersion": "v1",
    44      "kind": "Pod",
    45      "metadata": {
    46          "name": "foo",
    47          "namespace": "default"
    48      },
    49      "spec": {
    50          "containers": [
    51              {
    52                  "image": "bar:v2",
    53                  "name": "bar"
    54              }
    55          ]
    56      }
    57  }`),
    58  			},
    59  			OldObject: runtime.RawExtension{
    60  				Raw: []byte(`{
    61      "apiVersion": "v1",
    62      "kind": "Pod",
    63      "metadata": {
    64          "name": "foo",
    65          "namespace": "default"
    66      },
    67      "spec": {
    68          "containers": [
    69              {
    70                  "image": "bar:v1",
    71                  "name": "bar"
    72              }
    73          ]
    74      }
    75  }`),
    76  			},
    77  		},
    78  	}
    79  
    80  	It("should decode a valid admission request", func() {
    81  		By("extracting the object from the request")
    82  		var actualObj corev1.Pod
    83  		Expect(decoder.Decode(req, &actualObj)).To(Succeed())
    84  
    85  		By("verifying that all data is present in the object")
    86  		Expect(actualObj).To(Equal(corev1.Pod{
    87  			TypeMeta: metav1.TypeMeta{
    88  				APIVersion: "v1",
    89  				Kind:       "Pod",
    90  			},
    91  			ObjectMeta: metav1.ObjectMeta{
    92  				Name:      "foo",
    93  				Namespace: "default",
    94  			},
    95  			Spec: corev1.PodSpec{
    96  				Containers: []corev1.Container{
    97  					{Image: "bar:v2", Name: "bar"},
    98  				},
    99  			},
   100  		}))
   101  	})
   102  
   103  	It("should decode a valid RawExtension object", func() {
   104  		By("decoding the RawExtension object")
   105  		var actualObj corev1.Pod
   106  		Expect(decoder.DecodeRaw(req.OldObject, &actualObj)).To(Succeed())
   107  
   108  		By("verifying that all data is present in the object")
   109  		Expect(actualObj).To(Equal(corev1.Pod{
   110  			TypeMeta: metav1.TypeMeta{
   111  				APIVersion: "v1",
   112  				Kind:       "Pod",
   113  			},
   114  			ObjectMeta: metav1.ObjectMeta{
   115  				Name:      "foo",
   116  				Namespace: "default",
   117  			},
   118  			Spec: corev1.PodSpec{
   119  				Containers: []corev1.Container{
   120  					{Image: "bar:v1", Name: "bar"},
   121  				},
   122  			},
   123  		}))
   124  	})
   125  
   126  	// NOTE: This will only pass if a GVK is provided. An unstructered object without a GVK may succeed
   127  	// in decoding to an alternate type.
   128  	It("should fail to decode if the object in the request doesn't match the passed-in type", func() {
   129  		By("trying to extract a pod from the quest into a node")
   130  		Expect(decoder.Decode(req, &corev1.Node{})).NotTo(Succeed())
   131  
   132  		By("trying to extract a pod in RawExtension format into a node")
   133  		Expect(decoder.DecodeRaw(req.OldObject, &corev1.Node{})).NotTo(Succeed())
   134  	})
   135  
   136  	It("should be able to decode into an unstructured object", func() {
   137  		By("decoding the request into an unstructured object")
   138  		var target unstructured.Unstructured
   139  		Expect(decoder.Decode(req, &target)).To(Succeed())
   140  
   141  		By("sanity-checking the metadata on the output object")
   142  		Expect(target.Object["metadata"]).To(Equal(map[string]interface{}{
   143  			"name":      "foo",
   144  			"namespace": "default",
   145  		}))
   146  
   147  		By("decoding the RawExtension object into an unstructured object")
   148  		var target2 unstructured.Unstructured
   149  		Expect(decoder.DecodeRaw(req.Object, &target2)).To(Succeed())
   150  
   151  		By("sanity-checking the metadata on the output object")
   152  		Expect(target2.Object["metadata"]).To(Equal(map[string]interface{}{
   153  			"name":      "foo",
   154  			"namespace": "default",
   155  		}))
   156  	})
   157  
   158  	req2 := Request{
   159  		AdmissionRequest: admissionv1.AdmissionRequest{
   160  			Operation: "CREATE",
   161  			Object: runtime.RawExtension{
   162  				Raw: []byte(`{
   163      "metadata": {
   164          "name": "foo",
   165          "namespace": "default"
   166      },
   167      "spec": {
   168          "containers": [
   169              {
   170                  "image": "bar:v2",
   171                  "name": "bar"
   172              }
   173          ]
   174      }
   175  	}`),
   176  			},
   177  			OldObject: runtime.RawExtension{
   178  				Object: nil,
   179  			},
   180  		},
   181  	}
   182  
   183  	It("should decode a valid admission request without GVK", func() {
   184  		By("extracting the object from the request")
   185  		var target3 unstructured.Unstructured
   186  		Expect(decoder.DecodeRaw(req2.Object, &target3)).To(Succeed())
   187  	})
   188  })
   189  

View as plain text