...

Source file src/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion_test.go

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

     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 conversion_test
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"io"
    23  	"net/http"
    24  	"net/http/httptest"
    25  
    26  	. "github.com/onsi/ginkgo/v2"
    27  	. "github.com/onsi/gomega"
    28  
    29  	appsv1beta1 "k8s.io/api/apps/v1beta1"
    30  	apix "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    33  	"k8s.io/apimachinery/pkg/runtime"
    34  	kscheme "k8s.io/client-go/kubernetes/scheme"
    35  
    36  	"sigs.k8s.io/controller-runtime/pkg/webhook/conversion"
    37  	jobsv1 "sigs.k8s.io/controller-runtime/pkg/webhook/conversion/testdata/api/v1"
    38  	jobsv2 "sigs.k8s.io/controller-runtime/pkg/webhook/conversion/testdata/api/v2"
    39  	jobsv3 "sigs.k8s.io/controller-runtime/pkg/webhook/conversion/testdata/api/v3"
    40  )
    41  
    42  var _ = Describe("Conversion Webhook", func() {
    43  
    44  	var respRecorder *httptest.ResponseRecorder
    45  	var decoder *conversion.Decoder
    46  	var scheme *runtime.Scheme
    47  	var wh http.Handler
    48  
    49  	BeforeEach(func() {
    50  		respRecorder = &httptest.ResponseRecorder{
    51  			Body: bytes.NewBuffer(nil),
    52  		}
    53  
    54  		scheme = runtime.NewScheme()
    55  		Expect(kscheme.AddToScheme(scheme)).To(Succeed())
    56  		Expect(jobsv1.AddToScheme(scheme)).To(Succeed())
    57  		Expect(jobsv2.AddToScheme(scheme)).To(Succeed())
    58  		Expect(jobsv3.AddToScheme(scheme)).To(Succeed())
    59  
    60  		decoder = conversion.NewDecoder(scheme)
    61  		wh = conversion.NewWebhookHandler(scheme)
    62  	})
    63  
    64  	doRequest := func(convReq *apix.ConversionReview) *apix.ConversionReview {
    65  		var payload bytes.Buffer
    66  
    67  		Expect(json.NewEncoder(&payload).Encode(convReq)).Should(Succeed())
    68  
    69  		convReview := &apix.ConversionReview{}
    70  		req := &http.Request{
    71  			Body: io.NopCloser(bytes.NewReader(payload.Bytes())),
    72  		}
    73  		wh.ServeHTTP(respRecorder, req)
    74  		Expect(json.NewDecoder(respRecorder.Result().Body).Decode(convReview)).To(Succeed())
    75  		return convReview
    76  	}
    77  
    78  	makeV1Obj := func() *jobsv1.ExternalJob {
    79  		return &jobsv1.ExternalJob{
    80  			TypeMeta: metav1.TypeMeta{
    81  				Kind:       "ExternalJob",
    82  				APIVersion: "jobs.testprojects.kb.io/v1",
    83  			},
    84  			ObjectMeta: metav1.ObjectMeta{
    85  				Namespace: "default",
    86  				Name:      "obj-1",
    87  			},
    88  			Spec: jobsv1.ExternalJobSpec{
    89  				RunAt: "every 2 seconds",
    90  			},
    91  		}
    92  	}
    93  
    94  	makeV2Obj := func() *jobsv2.ExternalJob {
    95  		return &jobsv2.ExternalJob{
    96  			TypeMeta: metav1.TypeMeta{
    97  				Kind:       "ExternalJob",
    98  				APIVersion: "jobs.testprojects.kb.io/v2",
    99  			},
   100  			ObjectMeta: metav1.ObjectMeta{
   101  				Namespace: "default",
   102  				Name:      "obj-1",
   103  			},
   104  			Spec: jobsv2.ExternalJobSpec{
   105  				ScheduleAt: "every 2 seconds",
   106  			},
   107  		}
   108  	}
   109  
   110  	It("should convert spoke to hub successfully", func() {
   111  
   112  		v1Obj := makeV1Obj()
   113  
   114  		expected := &jobsv2.ExternalJob{
   115  			TypeMeta: metav1.TypeMeta{
   116  				Kind:       "ExternalJob",
   117  				APIVersion: "jobs.testprojects.kb.io/v2",
   118  			},
   119  			ObjectMeta: metav1.ObjectMeta{
   120  				Namespace: "default",
   121  				Name:      "obj-1",
   122  			},
   123  			Spec: jobsv2.ExternalJobSpec{
   124  				ScheduleAt: "every 2 seconds",
   125  			},
   126  		}
   127  
   128  		convReq := &apix.ConversionReview{
   129  			TypeMeta: metav1.TypeMeta{},
   130  			Request: &apix.ConversionRequest{
   131  				DesiredAPIVersion: "jobs.testprojects.kb.io/v2",
   132  				Objects: []runtime.RawExtension{
   133  					{
   134  						Object: v1Obj,
   135  					},
   136  				},
   137  			},
   138  		}
   139  
   140  		convReview := doRequest(convReq)
   141  
   142  		Expect(convReview.Response.ConvertedObjects).To(HaveLen(1))
   143  		Expect(convReview.Response.Result.Status).To(Equal(metav1.StatusSuccess))
   144  		got, _, err := decoder.Decode(convReview.Response.ConvertedObjects[0].Raw)
   145  		Expect(err).NotTo(HaveOccurred())
   146  		Expect(got).To(Equal(expected))
   147  	})
   148  
   149  	It("should convert hub to spoke successfully", func() {
   150  
   151  		v2Obj := makeV2Obj()
   152  
   153  		expected := &jobsv1.ExternalJob{
   154  			TypeMeta: metav1.TypeMeta{
   155  				Kind:       "ExternalJob",
   156  				APIVersion: "jobs.testprojects.kb.io/v1",
   157  			},
   158  			ObjectMeta: metav1.ObjectMeta{
   159  				Namespace: "default",
   160  				Name:      "obj-1",
   161  			},
   162  			Spec: jobsv1.ExternalJobSpec{
   163  				RunAt: "every 2 seconds",
   164  			},
   165  		}
   166  
   167  		convReq := &apix.ConversionReview{
   168  			TypeMeta: metav1.TypeMeta{},
   169  			Request: &apix.ConversionRequest{
   170  				DesiredAPIVersion: "jobs.testprojects.kb.io/v1",
   171  				Objects: []runtime.RawExtension{
   172  					{
   173  						Object: v2Obj,
   174  					},
   175  				},
   176  			},
   177  		}
   178  
   179  		convReview := doRequest(convReq)
   180  
   181  		Expect(convReview.Response.ConvertedObjects).To(HaveLen(1))
   182  		Expect(convReview.Response.Result.Status).To(Equal(metav1.StatusSuccess))
   183  		got, _, err := decoder.Decode(convReview.Response.ConvertedObjects[0].Raw)
   184  		Expect(err).NotTo(HaveOccurred())
   185  		Expect(got).To(Equal(expected))
   186  	})
   187  
   188  	It("should convert spoke to spoke successfully", func() {
   189  
   190  		v1Obj := makeV1Obj()
   191  
   192  		expected := &jobsv3.ExternalJob{
   193  			TypeMeta: metav1.TypeMeta{
   194  				Kind:       "ExternalJob",
   195  				APIVersion: "jobs.testprojects.kb.io/v3",
   196  			},
   197  			ObjectMeta: metav1.ObjectMeta{
   198  				Namespace: "default",
   199  				Name:      "obj-1",
   200  			},
   201  			Spec: jobsv3.ExternalJobSpec{
   202  				DeferredAt: "every 2 seconds",
   203  			},
   204  		}
   205  
   206  		convReq := &apix.ConversionReview{
   207  			TypeMeta: metav1.TypeMeta{},
   208  			Request: &apix.ConversionRequest{
   209  				DesiredAPIVersion: "jobs.testprojects.kb.io/v3",
   210  				Objects: []runtime.RawExtension{
   211  					{
   212  						Object: v1Obj,
   213  					},
   214  				},
   215  			},
   216  		}
   217  
   218  		convReview := doRequest(convReq)
   219  
   220  		Expect(convReview.Response.ConvertedObjects).To(HaveLen(1))
   221  		Expect(convReview.Response.Result.Status).To(Equal(metav1.StatusSuccess))
   222  		got, _, err := decoder.Decode(convReview.Response.ConvertedObjects[0].Raw)
   223  		Expect(err).NotTo(HaveOccurred())
   224  		Expect(got).To(Equal(expected))
   225  	})
   226  
   227  	It("should return error when dest/src objects belong to different API groups", func() {
   228  		v1Obj := makeV1Obj()
   229  
   230  		convReq := &apix.ConversionReview{
   231  			TypeMeta: metav1.TypeMeta{},
   232  			Request: &apix.ConversionRequest{
   233  				// request conversion for different group
   234  				DesiredAPIVersion: "jobss.example.org/v2",
   235  				Objects: []runtime.RawExtension{
   236  					{
   237  						Object: v1Obj,
   238  					},
   239  				},
   240  			},
   241  		}
   242  
   243  		convReview := doRequest(convReq)
   244  		Expect(convReview.Response.Result.Status).To(Equal("Failure"))
   245  		Expect(convReview.Response.ConvertedObjects).To(BeEmpty())
   246  	})
   247  
   248  	It("should return error when dest/src objects are of same type", func() {
   249  
   250  		v1Obj := makeV1Obj()
   251  
   252  		convReq := &apix.ConversionReview{
   253  			TypeMeta: metav1.TypeMeta{},
   254  			Request: &apix.ConversionRequest{
   255  				DesiredAPIVersion: "jobs.testprojects.kb.io/v1",
   256  				Objects: []runtime.RawExtension{
   257  					{
   258  						Object: v1Obj,
   259  					},
   260  				},
   261  			},
   262  		}
   263  
   264  		convReview := doRequest(convReq)
   265  		Expect(convReview.Response.Result.Status).To(Equal("Failure"))
   266  		Expect(convReview.Response.ConvertedObjects).To(BeEmpty())
   267  	})
   268  
   269  	It("should return error when the API group does not have a hub defined", func() {
   270  
   271  		v1Obj := &appsv1beta1.Deployment{
   272  			TypeMeta: metav1.TypeMeta{
   273  				Kind:       "Deployment",
   274  				APIVersion: "apps/v1beta1",
   275  			},
   276  			ObjectMeta: metav1.ObjectMeta{
   277  				Namespace: "default",
   278  				Name:      "obj-1",
   279  			},
   280  		}
   281  
   282  		convReq := &apix.ConversionReview{
   283  			TypeMeta: metav1.TypeMeta{},
   284  			Request: &apix.ConversionRequest{
   285  				DesiredAPIVersion: "apps/v1",
   286  				Objects: []runtime.RawExtension{
   287  					{
   288  						Object: v1Obj,
   289  					},
   290  				},
   291  			},
   292  		}
   293  
   294  		convReview := doRequest(convReq)
   295  		Expect(convReview.Response.Result.Status).To(Equal("Failure"))
   296  		Expect(convReview.Response.ConvertedObjects).To(BeEmpty())
   297  	})
   298  
   299  })
   300  
   301  var _ = Describe("IsConvertible", func() {
   302  
   303  	var scheme *runtime.Scheme
   304  
   305  	BeforeEach(func() {
   306  		scheme = runtime.NewScheme()
   307  
   308  		Expect(kscheme.AddToScheme(scheme)).To(Succeed())
   309  		Expect(jobsv1.AddToScheme(scheme)).To(Succeed())
   310  		Expect(jobsv2.AddToScheme(scheme)).To(Succeed())
   311  		Expect(jobsv3.AddToScheme(scheme)).To(Succeed())
   312  	})
   313  
   314  	It("should not error for uninitialized types", func() {
   315  		obj := &jobsv2.ExternalJob{}
   316  
   317  		ok, err := conversion.IsConvertible(scheme, obj)
   318  		Expect(err).NotTo(HaveOccurred())
   319  		Expect(ok).To(BeTrue())
   320  	})
   321  
   322  	It("should not error for unstructured types", func() {
   323  		obj := &unstructured.Unstructured{
   324  			Object: map[string]interface{}{
   325  				"kind":       "ExternalJob",
   326  				"apiVersion": "jobs.testprojects.kb.io/v2",
   327  			},
   328  		}
   329  
   330  		ok, err := conversion.IsConvertible(scheme, obj)
   331  		Expect(err).NotTo(HaveOccurred())
   332  		Expect(ok).To(BeTrue())
   333  	})
   334  
   335  	It("should return true for convertible types", func() {
   336  		obj := &jobsv2.ExternalJob{
   337  			TypeMeta: metav1.TypeMeta{
   338  				Kind:       "ExternalJob",
   339  				APIVersion: "jobs.testprojects.kb.io/v2",
   340  			},
   341  		}
   342  
   343  		ok, err := conversion.IsConvertible(scheme, obj)
   344  		Expect(err).NotTo(HaveOccurred())
   345  		Expect(ok).To(BeTrue())
   346  	})
   347  
   348  	It("should return false for a non convertible type", func() {
   349  		obj := &appsv1beta1.Deployment{
   350  			TypeMeta: metav1.TypeMeta{
   351  				Kind:       "Deployment",
   352  				APIVersion: "apps/v1beta1",
   353  			},
   354  		}
   355  
   356  		ok, err := conversion.IsConvertible(scheme, obj)
   357  		Expect(err).NotTo(HaveOccurred())
   358  		Expect(ok).ToNot(BeTrue())
   359  	})
   360  })
   361  

View as plain text