github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/apis/meta/v1/unstructured/unstructured_conversion_test.go (about)

     1  /*
     2  Copyright 2019 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 unstructured_test
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  
    28  	metav1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1"
    29  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1/unstructured"
    30  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/testapigroup"
    31  	testapigroupv1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/testapigroup/v1"
    32  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime"
    33  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema"
    34  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/test"
    35  )
    36  
    37  func TestObjectToUnstructuredConversion(t *testing.T) {
    38  	scheme, _ := test.TestScheme()
    39  	testCases := []struct {
    40  		name                          string
    41  		objectToConvert               runtime.Object
    42  		expectedErr                   error
    43  		expectedConvertedUnstructured *unstructured.Unstructured
    44  	}{
    45  		{
    46  			name:                          "convert nil object to unstructured should fail",
    47  			objectToConvert:               nil,
    48  			expectedErr:                   fmt.Errorf("unable to convert object type <nil> to Unstructured, must be a runtime.Object"),
    49  			expectedConvertedUnstructured: &unstructured.Unstructured{},
    50  		},
    51  		{
    52  			name:            "convert versioned empty object to unstructured should work",
    53  			objectToConvert: &testapigroupv1.Carp{},
    54  			expectedConvertedUnstructured: &unstructured.Unstructured{
    55  				Object: map[string]interface{}{
    56  					"apiVersion": "v1",
    57  					"kind":       "Carp",
    58  					"metadata": map[string]interface{}{
    59  						"creationTimestamp": nil,
    60  					},
    61  					"spec":   map[string]interface{}{},
    62  					"status": map[string]interface{}{},
    63  				},
    64  			},
    65  		},
    66  		{
    67  			name: "convert valid versioned object to unstructured should work",
    68  			objectToConvert: &testapigroupv1.Carp{
    69  				ObjectMeta: metav1.ObjectMeta{
    70  					Name: "noxu",
    71  				},
    72  				Spec: testapigroupv1.CarpSpec{
    73  					Hostname: "example.com",
    74  				},
    75  			},
    76  			expectedConvertedUnstructured: &unstructured.Unstructured{
    77  				Object: map[string]interface{}{
    78  					"apiVersion": "v1",
    79  					"kind":       "Carp",
    80  					"metadata": map[string]interface{}{
    81  						"creationTimestamp": nil,
    82  						"name":              "noxu",
    83  					},
    84  					"spec": map[string]interface{}{
    85  						"hostname": "example.com",
    86  					},
    87  					"status": map[string]interface{}{},
    88  				},
    89  			},
    90  		},
    91  		{
    92  			name:            "convert hub-versioned object to unstructured should fail",
    93  			objectToConvert: &testapigroup.Carp{},
    94  			expectedErr:     fmt.Errorf("unable to convert the internal object type *testapigroup.Carp to Unstructured without providing a preferred version to convert to"),
    95  		},
    96  	}
    97  	for _, testCase := range testCases {
    98  		t.Run(testCase.name, func(t *testing.T) {
    99  			outUnstructured := &unstructured.Unstructured{}
   100  			err := scheme.Convert(testCase.objectToConvert, outUnstructured, nil)
   101  			if err != nil {
   102  				assert.Equal(t, testCase.expectedErr, err)
   103  				return
   104  			}
   105  			assert.Equal(t, testCase.expectedConvertedUnstructured, outUnstructured)
   106  		})
   107  	}
   108  }
   109  
   110  func TestUnstructuredToObjectConversion(t *testing.T) {
   111  	scheme, _ := test.TestScheme()
   112  	testCases := []struct {
   113  		name                    string
   114  		unstructuredToConvert   *unstructured.Unstructured
   115  		convertingObject        runtime.Object
   116  		expectPanic             bool
   117  		expectedErrFunc         func(err error) bool
   118  		expectedConvertedObject runtime.Object
   119  	}{
   120  		{
   121  			name: "convert empty unstructured w/o gvk to versioned object should fail",
   122  			unstructuredToConvert: &unstructured.Unstructured{
   123  				Object: map[string]interface{}{},
   124  			},
   125  			convertingObject: &testapigroupv1.Carp{},
   126  			expectedErrFunc: func(err error) bool {
   127  				return reflect.DeepEqual(err, runtime.NewMissingKindErr("unstructured object has no kind"))
   128  			},
   129  		},
   130  		{
   131  			name: "convert empty versioned unstructured to versioned object should work",
   132  			unstructuredToConvert: &unstructured.Unstructured{
   133  				Object: map[string]interface{}{
   134  					"apiVersion": "v1",
   135  					"kind":       "Carp",
   136  				},
   137  			},
   138  			convertingObject:        &testapigroupv1.Carp{},
   139  			expectedConvertedObject: &testapigroupv1.Carp{},
   140  		},
   141  		{
   142  			name: "convert empty unstructured w/o gvk to versioned object should fail",
   143  			unstructuredToConvert: &unstructured.Unstructured{
   144  				Object: map[string]interface{}{},
   145  			},
   146  			convertingObject: &testapigroupv1.Carp{},
   147  			expectedErrFunc: func(err error) bool {
   148  				return reflect.DeepEqual(err, runtime.NewMissingKindErr("unstructured object has no kind"))
   149  			},
   150  		},
   151  		{
   152  			name: "convert valid versioned unstructured to versioned object should work",
   153  			unstructuredToConvert: &unstructured.Unstructured{
   154  				Object: map[string]interface{}{
   155  					"apiVersion": "v1",
   156  					"kind":       "Carp",
   157  					"metadata": map[string]interface{}{
   158  						"creationTimestamp": nil,
   159  						"name":              "noxu",
   160  					},
   161  					"spec": map[string]interface{}{
   162  						"hostname": "example.com",
   163  					},
   164  					"status": map[string]interface{}{},
   165  				},
   166  			},
   167  			convertingObject: &testapigroupv1.Carp{},
   168  			expectedConvertedObject: &testapigroupv1.Carp{
   169  				ObjectMeta: metav1.ObjectMeta{
   170  					Name: "noxu",
   171  				},
   172  				Spec: testapigroupv1.CarpSpec{
   173  					Hostname: "example.com",
   174  				},
   175  			},
   176  		},
   177  		{
   178  			name: "convert valid versioned unstructured to hub-versioned object should work",
   179  			unstructuredToConvert: &unstructured.Unstructured{
   180  				Object: map[string]interface{}{
   181  					"apiVersion": "v1",
   182  					"kind":       "Carp",
   183  					"metadata": map[string]interface{}{
   184  						"creationTimestamp": nil,
   185  						"name":              "noxu",
   186  					},
   187  					"spec": map[string]interface{}{
   188  						"hostname": "example.com",
   189  					},
   190  					"status": map[string]interface{}{},
   191  				},
   192  			},
   193  			convertingObject: &testapigroup.Carp{},
   194  			expectedConvertedObject: &testapigroup.Carp{
   195  				ObjectMeta: metav1.ObjectMeta{
   196  					Name: "noxu",
   197  				},
   198  				Spec: testapigroup.CarpSpec{
   199  					Hostname: "example.com",
   200  				},
   201  			},
   202  		},
   203  		{
   204  			name: "convert unexisting-versioned unstructured to hub-versioned object should fail",
   205  			unstructuredToConvert: &unstructured.Unstructured{
   206  				Object: map[string]interface{}{
   207  					"apiVersion": "v9",
   208  					"kind":       "Carp",
   209  					"metadata": map[string]interface{}{
   210  						"creationTimestamp": nil,
   211  						"name":              "noxu",
   212  					},
   213  					"spec": map[string]interface{}{
   214  						"hostname": "example.com",
   215  					},
   216  					"status": map[string]interface{}{},
   217  				},
   218  			},
   219  			convertingObject: &testapigroup.Carp{},
   220  			expectedErrFunc: func(err error) bool {
   221  				return reflect.DeepEqual(err, runtime.NewNotRegisteredGVKErrForTarget(
   222  					scheme.Name(),
   223  					schema.GroupVersionKind{Group: "", Version: "v9", Kind: "Carp"},
   224  					nil,
   225  				))
   226  			},
   227  		},
   228  		{
   229  			name: "convert valid versioned unstructured to object w/ a mismatching kind should fail",
   230  			unstructuredToConvert: &unstructured.Unstructured{
   231  				Object: map[string]interface{}{
   232  					"apiVersion": "v1",
   233  					"kind":       "Carp",
   234  					"metadata": map[string]interface{}{
   235  						"creationTimestamp": nil,
   236  						"name":              "noxu",
   237  					},
   238  					"spec": map[string]interface{}{
   239  						"hostname": "example.com",
   240  					},
   241  					"status": map[string]interface{}{},
   242  				},
   243  			},
   244  			convertingObject: &metav1.CreateOptions{},
   245  			expectedErrFunc: func(err error) bool {
   246  				return strings.HasPrefix(err.Error(), "converting (v1.Carp) to (v1.CreateOptions):")
   247  			},
   248  		},
   249  	}
   250  	for _, testCase := range testCases {
   251  		t.Run(testCase.name, func(t *testing.T) {
   252  			defer func() {
   253  				v := recover()
   254  				assert.Equal(t, testCase.expectPanic, v != nil, "unexpected panic")
   255  			}()
   256  			outObject := testCase.convertingObject.DeepCopyObject()
   257  			// Convert by specifying destination object
   258  			err := scheme.Convert(testCase.unstructuredToConvert, outObject, nil)
   259  			if err != nil {
   260  				if testCase.expectedErrFunc != nil {
   261  					if !testCase.expectedErrFunc(err) {
   262  						t.Errorf("error mismatched: %v", err)
   263  					}
   264  				}
   265  				return
   266  			}
   267  			assert.Equal(t, testCase.expectedConvertedObject, outObject)
   268  		})
   269  	}
   270  }
   271  
   272  func TestUnstructuredToGVConversion(t *testing.T) {
   273  	scheme, _ := test.TestScheme()
   274  	// HACK: registering fake internal/v1beta1 api
   275  	scheme.AddKnownTypes(schema.GroupVersion{Group: "foo", Version: "v1beta1"}, &testapigroup.Carp{})
   276  	scheme.AddKnownTypes(schema.GroupVersion{Group: "foo", Version: "__internal"}, &testapigroup.Carp{})
   277  
   278  	testCases := []struct {
   279  		name                    string
   280  		unstructuredToConvert   *unstructured.Unstructured
   281  		targetGV                schema.GroupVersion
   282  		expectPanic             bool
   283  		expectedErrFunc         func(err error) bool
   284  		expectedConvertedObject runtime.Object
   285  	}{
   286  		{
   287  			name: "convert versioned unstructured to valid external version should work",
   288  			unstructuredToConvert: &unstructured.Unstructured{
   289  				Object: map[string]interface{}{
   290  					"apiVersion": "v1",
   291  					"kind":       "Carp",
   292  				},
   293  			},
   294  			targetGV: schema.GroupVersion{Group: "", Version: "v1"},
   295  			expectedConvertedObject: &testapigroupv1.Carp{
   296  				TypeMeta: metav1.TypeMeta{
   297  					APIVersion: "v1",
   298  					Kind:       "Carp",
   299  				},
   300  			},
   301  		},
   302  		{
   303  			name: "convert hub-versioned unstructured to hub version should work",
   304  			unstructuredToConvert: &unstructured.Unstructured{
   305  				Object: map[string]interface{}{
   306  					"apiVersion": "__internal",
   307  					"kind":       "Carp",
   308  				},
   309  			},
   310  			targetGV:                schema.GroupVersion{Group: "", Version: "__internal"},
   311  			expectedConvertedObject: &testapigroup.Carp{},
   312  		},
   313  		{
   314  			name: "convert empty unstructured w/o gvk to versioned should fail",
   315  			unstructuredToConvert: &unstructured.Unstructured{
   316  				Object: map[string]interface{}{},
   317  			},
   318  			targetGV: schema.GroupVersion{Group: "", Version: "v1"},
   319  			expectedErrFunc: func(err error) bool {
   320  				return reflect.DeepEqual(err, runtime.NewMissingKindErr("unstructured object has no kind"))
   321  			},
   322  			expectedConvertedObject: nil,
   323  		},
   324  		{
   325  			name: "convert versioned unstructured to mismatching external version should fail",
   326  			unstructuredToConvert: &unstructured.Unstructured{
   327  				Object: map[string]interface{}{
   328  					"apiVersion": "v1",
   329  					"kind":       "Carp",
   330  				},
   331  			},
   332  			targetGV: schema.GroupVersion{Group: "foo", Version: "v1beta1"},
   333  			expectedErrFunc: func(err error) bool {
   334  				return reflect.DeepEqual(err, runtime.NewNotRegisteredErrForTarget(
   335  					scheme.Name(), reflect.TypeOf(testapigroupv1.Carp{}), schema.GroupVersion{Group: "foo", Version: "v1beta1"}))
   336  			},
   337  			expectedConvertedObject: nil,
   338  		},
   339  		{
   340  			name: "convert versioned unstructured to mismatching internal version should fail",
   341  			unstructuredToConvert: &unstructured.Unstructured{
   342  				Object: map[string]interface{}{
   343  					"apiVersion": "v1",
   344  					"kind":       "Carp",
   345  				},
   346  			},
   347  			targetGV: schema.GroupVersion{Group: "foo", Version: "__internal"},
   348  			expectedErrFunc: func(err error) bool {
   349  				return reflect.DeepEqual(err, runtime.NewNotRegisteredErrForTarget(
   350  					scheme.Name(), reflect.TypeOf(testapigroupv1.Carp{}), schema.GroupVersion{Group: "foo", Version: "__internal"}))
   351  			},
   352  			expectedConvertedObject: nil,
   353  		},
   354  		{
   355  			name: "convert valid versioned unstructured to its own version should work",
   356  			unstructuredToConvert: &unstructured.Unstructured{
   357  				Object: map[string]interface{}{
   358  					"apiVersion": "v1",
   359  					"kind":       "Carp",
   360  					"metadata": map[string]interface{}{
   361  						"creationTimestamp": nil,
   362  						"name":              "noxu",
   363  					},
   364  					"spec": map[string]interface{}{
   365  						"hostname": "example.com",
   366  					},
   367  					"status": map[string]interface{}{},
   368  				},
   369  			},
   370  			targetGV: schema.GroupVersion{Group: "", Version: "v1"},
   371  			expectedConvertedObject: &testapigroupv1.Carp{
   372  				TypeMeta: metav1.TypeMeta{
   373  					APIVersion: "v1",
   374  					Kind:       "Carp",
   375  				},
   376  				ObjectMeta: metav1.ObjectMeta{
   377  					Name: "noxu",
   378  				},
   379  				Spec: testapigroupv1.CarpSpec{
   380  					Hostname: "example.com",
   381  				},
   382  			},
   383  		},
   384  		{
   385  			name: "convert valid versioned unstructured to hub-version should work ignoring type meta",
   386  			unstructuredToConvert: &unstructured.Unstructured{
   387  				Object: map[string]interface{}{
   388  					"apiVersion": "v1",
   389  					"kind":       "Carp",
   390  					"metadata": map[string]interface{}{
   391  						"creationTimestamp": nil,
   392  						"name":              "noxu",
   393  					},
   394  					"spec": map[string]interface{}{
   395  						"hostname": "example.com",
   396  					},
   397  					"status": map[string]interface{}{},
   398  				},
   399  			},
   400  			targetGV: schema.GroupVersion{Group: "", Version: "__internal"},
   401  			expectedConvertedObject: &testapigroup.Carp{
   402  				ObjectMeta: metav1.ObjectMeta{
   403  					Name: "noxu",
   404  				},
   405  				Spec: testapigroup.CarpSpec{
   406  					Hostname: "example.com",
   407  				},
   408  			},
   409  		},
   410  		{
   411  			name: "convert valid versioned unstructured to unexisting version should fail",
   412  			unstructuredToConvert: &unstructured.Unstructured{
   413  				Object: map[string]interface{}{
   414  					"apiVersion": "v1",
   415  					"kind":       "Carp",
   416  					"metadata": map[string]interface{}{
   417  						"creationTimestamp": nil,
   418  						"name":              "noxu",
   419  					},
   420  					"spec": map[string]interface{}{
   421  						"hostname": "example.com",
   422  					},
   423  					"status": map[string]interface{}{},
   424  				},
   425  			},
   426  			targetGV: schema.GroupVersion{Group: "", Version: "v9"},
   427  			expectedErrFunc: func(err error) bool {
   428  				return reflect.DeepEqual(err, runtime.NewNotRegisteredGVKErrForTarget(
   429  					scheme.Name(),
   430  					schema.GroupVersionKind{Group: "", Version: "v9", Kind: "Carp"},
   431  					nil,
   432  				))
   433  			},
   434  			expectedConvertedObject: nil,
   435  		},
   436  	}
   437  	for _, testCase := range testCases {
   438  		t.Run(testCase.name, func(t *testing.T) {
   439  			defer func() {
   440  				v := recover()
   441  				assert.Equal(t, testCase.expectPanic, v != nil, "unexpected panic")
   442  			}()
   443  			// Convert by specifying destination object
   444  			outObject, err := scheme.ConvertToVersion(testCase.unstructuredToConvert, testCase.targetGV)
   445  			if testCase.expectedErrFunc != nil {
   446  				if !testCase.expectedErrFunc(err) {
   447  					t.Errorf("error mismatched: %v", err)
   448  				}
   449  			}
   450  			assert.Equal(t, testCase.expectedConvertedObject, outObject)
   451  		})
   452  	}
   453  }
   454  
   455  func TestUnstructuredToUnstructuredConversion(t *testing.T) {
   456  	// eventually, we don't want any inter-unstructured conversion happen, but for now, the conversion
   457  	// just copy/pastes
   458  	scheme, _ := test.TestScheme()
   459  	inUnstructured := &unstructured.Unstructured{
   460  		Object: map[string]interface{}{
   461  			"apiVersion": "v1",
   462  			"kind":       "Carp",
   463  		},
   464  	}
   465  	outUnstructured := &unstructured.Unstructured{}
   466  	err := scheme.Convert(inUnstructured, outUnstructured, nil)
   467  	assert.NoError(t, err)
   468  	assert.Equal(t, inUnstructured, outUnstructured)
   469  }
   470  
   471  func benchmarkCarp() *testapigroupv1.Carp {
   472  	t := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
   473  	return &testapigroupv1.Carp{
   474  		ObjectMeta: metav1.ObjectMeta{
   475  			Name:      "name",
   476  			Namespace: "namespace",
   477  		},
   478  		Spec: testapigroupv1.CarpSpec{
   479  			RestartPolicy: "restart",
   480  			NodeSelector: map[string]string{
   481  				"label1": "value1",
   482  				"label2": "value2",
   483  			},
   484  			ServiceAccountName: "service-account",
   485  			HostNetwork:        false,
   486  			HostPID:            true,
   487  			Subdomain:          "hostname.subdomain.namespace.svc.domain",
   488  		},
   489  		Status: testapigroupv1.CarpStatus{
   490  			Phase: "phase",
   491  			Conditions: []testapigroupv1.CarpCondition{
   492  				{
   493  					Type:               "condition1",
   494  					Status:             "true",
   495  					LastProbeTime:      t,
   496  					LastTransitionTime: t,
   497  					Reason:             "reason",
   498  					Message:            "message",
   499  				},
   500  			},
   501  			Message: "message",
   502  			Reason:  "reason",
   503  			HostIP:  "1.2.3.4",
   504  		},
   505  	}
   506  }
   507  
   508  func BenchmarkToUnstructured(b *testing.B) {
   509  	carp := benchmarkCarp()
   510  	converter := runtime.DefaultUnstructuredConverter
   511  	b.ResetTimer()
   512  
   513  	for i := 0; i < b.N; i++ {
   514  		result, err := converter.ToUnstructured(carp)
   515  		if err != nil {
   516  			b.Fatalf("Unexpected conversion error: %v", err)
   517  		}
   518  		if len(result) != 3 {
   519  			b.Errorf("Unexpected conversion result: %#v", result)
   520  		}
   521  	}
   522  }
   523  
   524  func BenchmarkFromUnstructured(b *testing.B) {
   525  	carp := benchmarkCarp()
   526  	converter := runtime.DefaultUnstructuredConverter
   527  	unstr, err := converter.ToUnstructured(carp)
   528  	if err != nil {
   529  		b.Fatalf("Unexpected conversion error: %v", err)
   530  	}
   531  	b.ResetTimer()
   532  
   533  	for i := 0; i < b.N; i++ {
   534  		result := testapigroupv1.Carp{}
   535  		if err := converter.FromUnstructured(unstr, &result); err != nil {
   536  			b.Fatalf("Unexpected conversion error: %v", err)
   537  		}
   538  		if result.Status.Phase != "phase" {
   539  			b.Errorf("Unexpected conversion result: %#v", result)
   540  		}
   541  	}
   542  }