k8s.io/kubernetes@v1.29.3/pkg/api/testing/unstructured_test.go (about)

     1  /*
     2  Copyright 2017 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 testing
    18  
    19  import (
    20  	"math/rand"
    21  	"reflect"
    22  	"testing"
    23  
    24  	"github.com/google/go-cmp/cmp"
    25  	fuzz "github.com/google/gofuzz"
    26  
    27  	v1 "k8s.io/api/core/v1"
    28  	"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
    29  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    30  	"k8s.io/apimachinery/pkg/api/meta"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	metaunstruct "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    33  	"k8s.io/apimachinery/pkg/runtime"
    34  	"k8s.io/apimachinery/pkg/runtime/schema"
    35  	"k8s.io/apimachinery/pkg/util/json"
    36  	"k8s.io/kubernetes/pkg/api/legacyscheme"
    37  	api "k8s.io/kubernetes/pkg/apis/core"
    38  )
    39  
    40  func doRoundTrip(t *testing.T, internalVersion schema.GroupVersion, externalVersion schema.GroupVersion, kind string) {
    41  	// We do fuzzing on the internal version of the object, and only then
    42  	// convert to the external version. This is because custom fuzzing
    43  	// function are only supported for internal objects.
    44  	internalObj, err := legacyscheme.Scheme.New(internalVersion.WithKind(kind))
    45  	if err != nil {
    46  		t.Fatalf("Couldn't create internal object %v: %v", kind, err)
    47  	}
    48  	seed := rand.Int63()
    49  	fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(seed), legacyscheme.Codecs).
    50  		// We are explicitly overwriting custom fuzzing functions, to ensure
    51  		// that InitContainers and their statuses are not generated. This is
    52  		// because in this test we are simply doing json operations, in which
    53  		// those disappear.
    54  		Funcs(
    55  			func(s *api.PodSpec, c fuzz.Continue) {
    56  				c.FuzzNoCustom(s)
    57  				s.InitContainers = nil
    58  			},
    59  			func(s *api.PodStatus, c fuzz.Continue) {
    60  				c.FuzzNoCustom(s)
    61  				s.InitContainerStatuses = nil
    62  			},
    63  		).Fuzz(internalObj)
    64  
    65  	item, err := legacyscheme.Scheme.New(externalVersion.WithKind(kind))
    66  	if err != nil {
    67  		t.Fatalf("Couldn't create external object %v: %v", kind, err)
    68  	}
    69  	if err := legacyscheme.Scheme.Convert(internalObj, item, nil); err != nil {
    70  		t.Fatalf("Conversion for %v failed: %v", kind, err)
    71  	}
    72  
    73  	data, err := json.Marshal(item)
    74  	if err != nil {
    75  		t.Errorf("Error when marshaling object: %v", err)
    76  		return
    77  	}
    78  	unstr := make(map[string]interface{})
    79  	err = json.Unmarshal(data, &unstr)
    80  	if err != nil {
    81  		t.Errorf("Error when unmarshaling to unstructured: %v", err)
    82  		return
    83  	}
    84  
    85  	data, err = json.Marshal(unstr)
    86  	if err != nil {
    87  		t.Errorf("Error when marshaling unstructured: %v", err)
    88  		return
    89  	}
    90  	unmarshalledObj := reflect.New(reflect.TypeOf(item).Elem()).Interface()
    91  	err = json.Unmarshal(data, &unmarshalledObj)
    92  	if err != nil {
    93  		t.Errorf("Error when unmarshaling to object: %v", err)
    94  		return
    95  	}
    96  	if !apiequality.Semantic.DeepEqual(item, unmarshalledObj) {
    97  		t.Errorf("Object changed during JSON operations, diff: %v", cmp.Diff(item, unmarshalledObj))
    98  		return
    99  	}
   100  
   101  	newUnstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(item)
   102  	if err != nil {
   103  		t.Errorf("ToUnstructured failed: %v", err)
   104  		return
   105  	}
   106  
   107  	newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object)
   108  	err = runtime.DefaultUnstructuredConverter.FromUnstructured(newUnstr, newObj)
   109  	if err != nil {
   110  		t.Errorf("FromUnstructured failed: %v", err)
   111  		return
   112  	}
   113  
   114  	if !apiequality.Semantic.DeepEqual(item, newObj) {
   115  		t.Errorf("Object changed, diff: %v", cmp.Diff(item, newObj))
   116  	}
   117  }
   118  
   119  func TestRoundTrip(t *testing.T) {
   120  	for gvk := range legacyscheme.Scheme.AllKnownTypes() {
   121  		if nonRoundTrippableTypes.Has(gvk.Kind) {
   122  			continue
   123  		}
   124  		if gvk.Version == runtime.APIVersionInternal {
   125  			continue
   126  		}
   127  		t.Logf("Testing: %v in %v", gvk.Kind, gvk.GroupVersion().String())
   128  		for i := 0; i < 50; i++ {
   129  			doRoundTrip(t, schema.GroupVersion{Group: gvk.Group, Version: runtime.APIVersionInternal}, gvk.GroupVersion(), gvk.Kind)
   130  			if t.Failed() {
   131  				break
   132  			}
   133  		}
   134  	}
   135  }
   136  
   137  func TestRoundTripWithEmptyCreationTimestamp(t *testing.T) {
   138  	for gvk := range legacyscheme.Scheme.AllKnownTypes() {
   139  		if nonRoundTrippableTypes.Has(gvk.Kind) {
   140  			continue
   141  		}
   142  		if gvk.Version == runtime.APIVersionInternal {
   143  			continue
   144  		}
   145  
   146  		item, err := legacyscheme.Scheme.New(gvk)
   147  		if err != nil {
   148  			t.Fatalf("Couldn't create external object %v: %v", gvk, err)
   149  		}
   150  		t.Logf("Testing: %v in %v", gvk.Kind, gvk.GroupVersion().String())
   151  
   152  		unstrBody, err := runtime.DefaultUnstructuredConverter.ToUnstructured(item)
   153  		if err != nil {
   154  			t.Fatalf("ToUnstructured failed: %v", err)
   155  		}
   156  
   157  		unstructObj := &metaunstruct.Unstructured{}
   158  		unstructObj.Object = unstrBody
   159  
   160  		if meta, err := meta.Accessor(unstructObj); err == nil {
   161  			meta.SetCreationTimestamp(metav1.Time{})
   162  		} else {
   163  			t.Fatalf("Unable to set creation timestamp: %v", err)
   164  		}
   165  
   166  		// attempt to re-convert unstructured object - conversion should not fail
   167  		// based on empty metadata fields, such as creationTimestamp
   168  		newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object)
   169  		err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructObj.Object, newObj)
   170  		if err != nil {
   171  			t.Fatalf("FromUnstructured failed: %v", err)
   172  		}
   173  	}
   174  }
   175  
   176  func BenchmarkToUnstructured(b *testing.B) {
   177  	items := benchmarkItems(b)
   178  	size := len(items)
   179  	convertor := runtime.DefaultUnstructuredConverter
   180  	b.ResetTimer()
   181  	for i := 0; i < b.N; i++ {
   182  		unstr, err := convertor.ToUnstructured(&items[i%size])
   183  		if err != nil || unstr == nil {
   184  			b.Fatalf("unexpected error: %v", err)
   185  		}
   186  	}
   187  	b.StopTimer()
   188  }
   189  
   190  func BenchmarkFromUnstructured(b *testing.B) {
   191  	items := benchmarkItems(b)
   192  	convertor := runtime.DefaultUnstructuredConverter
   193  	var unstr []map[string]interface{}
   194  	for i := range items {
   195  		item, err := convertor.ToUnstructured(&items[i])
   196  		if err != nil || item == nil {
   197  			b.Fatalf("unexpected error: %v", err)
   198  		}
   199  		unstr = append(unstr, item)
   200  	}
   201  	size := len(items)
   202  	b.ResetTimer()
   203  	for i := 0; i < b.N; i++ {
   204  		obj := v1.Pod{}
   205  		if err := convertor.FromUnstructured(unstr[i%size], &obj); err != nil {
   206  			b.Fatalf("unexpected error: %v", err)
   207  		}
   208  	}
   209  	b.StopTimer()
   210  }
   211  
   212  func BenchmarkToUnstructuredViaJSON(b *testing.B) {
   213  	items := benchmarkItems(b)
   214  	var data [][]byte
   215  	for i := range items {
   216  		item, err := json.Marshal(&items[i])
   217  		if err != nil {
   218  			b.Fatalf("unexpected error: %v", err)
   219  		}
   220  		data = append(data, item)
   221  	}
   222  	size := len(items)
   223  	b.ResetTimer()
   224  	for i := 0; i < b.N; i++ {
   225  		unstr := map[string]interface{}{}
   226  		if err := json.Unmarshal(data[i%size], &unstr); err != nil {
   227  			b.Fatalf("unexpected error: %v", err)
   228  		}
   229  	}
   230  	b.StopTimer()
   231  }
   232  
   233  func BenchmarkFromUnstructuredViaJSON(b *testing.B) {
   234  	items := benchmarkItems(b)
   235  	var unstr []map[string]interface{}
   236  	for i := range items {
   237  		data, err := json.Marshal(&items[i])
   238  		if err != nil {
   239  			b.Fatalf("unexpected error: %v", err)
   240  		}
   241  		item := map[string]interface{}{}
   242  		if err := json.Unmarshal(data, &item); err != nil {
   243  			b.Fatalf("unexpected error: %v", err)
   244  		}
   245  		unstr = append(unstr, item)
   246  	}
   247  	size := len(items)
   248  	b.ResetTimer()
   249  	for i := 0; i < b.N; i++ {
   250  		item, err := json.Marshal(unstr[i%size])
   251  		if err != nil {
   252  			b.Fatalf("unexpected error: %v", err)
   253  		}
   254  		obj := v1.Pod{}
   255  		if err := json.Unmarshal(item, &obj); err != nil {
   256  			b.Fatalf("unexpected error: %v", err)
   257  		}
   258  	}
   259  	b.StopTimer()
   260  }