...

Source file src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/cbor_test.go

Documentation: k8s.io/apimachinery/pkg/runtime/serializer/cbor

     1  /*
     2  Copyright 2024 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  // The tests in this package focus on the correctness of its implementation of
    18  // runtime.Serializer. The specific behavior of marshaling Go values to CBOR bytes and back is
    19  // tested in the ./internal/modes package, which is used both by the Serializer implementation and
    20  // the package-scoped Marshal/Unmarshal functions in the ./direct package.
    21  package cbor
    22  
    23  import (
    24  	"bytes"
    25  	"encoding/hex"
    26  	"errors"
    27  	"io"
    28  	"reflect"
    29  	"testing"
    30  
    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  	"k8s.io/apimachinery/pkg/runtime/schema"
    35  	"k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes"
    36  
    37  	"github.com/google/go-cmp/cmp"
    38  )
    39  
    40  func TestRecognizesData(t *testing.T) {
    41  	for _, tc := range []struct {
    42  		in         []byte
    43  		recognizes bool
    44  	}{
    45  		{
    46  			in:         nil,
    47  			recognizes: false,
    48  		},
    49  		{
    50  			in:         []byte{},
    51  			recognizes: false,
    52  		},
    53  		{
    54  			in:         []byte{0xd9},
    55  			recognizes: false,
    56  		},
    57  		{
    58  			in:         []byte{0xd9, 0xd9},
    59  			recognizes: false,
    60  		},
    61  		{
    62  			in:         []byte{0xd9, 0xd9, 0xf7},
    63  			recognizes: true,
    64  		},
    65  		{
    66  			in:         []byte{0xff, 0xff, 0xff},
    67  			recognizes: false,
    68  		},
    69  		{
    70  			in:         []byte{0xd9, 0xd9, 0xf7, 0x01, 0x02, 0x03},
    71  			recognizes: true,
    72  		},
    73  		{
    74  			in:         []byte{0xff, 0xff, 0xff, 0x01, 0x02, 0x03},
    75  			recognizes: false,
    76  		},
    77  	} {
    78  		t.Run(hex.EncodeToString(tc.in), func(t *testing.T) {
    79  			s := NewSerializer(nil, nil)
    80  			recognizes, unknown, err := s.RecognizesData(tc.in)
    81  			if recognizes != tc.recognizes {
    82  				t.Errorf("expected recognized to be %t, got %t", tc.recognizes, recognizes)
    83  			}
    84  			if unknown {
    85  				t.Error("expected unknown to be false, got true")
    86  			}
    87  			if err != nil {
    88  				t.Errorf("expected nil error, got: %v", err)
    89  			}
    90  		})
    91  	}
    92  }
    93  
    94  type stubWriter struct {
    95  	n   int
    96  	err error
    97  }
    98  
    99  func (w stubWriter) Write([]byte) (int, error) {
   100  	return w.n, w.err
   101  }
   102  
   103  // anyObject wraps arbitrary concrete values to be encoded or decoded.
   104  type anyObject struct {
   105  	Value interface{}
   106  }
   107  
   108  func (p anyObject) GetObjectKind() schema.ObjectKind {
   109  	return schema.EmptyObjectKind
   110  }
   111  
   112  func (anyObject) DeepCopyObject() runtime.Object {
   113  	panic("unimplemented")
   114  }
   115  
   116  func (p anyObject) MarshalCBOR() ([]byte, error) {
   117  	return modes.Encode.Marshal(p.Value)
   118  }
   119  
   120  func (p *anyObject) UnmarshalCBOR(in []byte) error {
   121  	return modes.Decode.Unmarshal(in, &p.Value)
   122  }
   123  
   124  func TestEncode(t *testing.T) {
   125  	for _, tc := range []struct {
   126  		name           string
   127  		in             runtime.Object
   128  		assertOnWriter func() (io.Writer, func(*testing.T))
   129  		assertOnError  func(*testing.T, error)
   130  	}{
   131  		{
   132  			name: "io error writing self described cbor tag",
   133  			assertOnWriter: func() (io.Writer, func(*testing.T)) {
   134  				return stubWriter{err: io.ErrShortWrite}, func(*testing.T) {}
   135  			},
   136  			assertOnError: func(t *testing.T, err error) {
   137  				if !errors.Is(err, io.ErrShortWrite) {
   138  					t.Errorf("expected io.ErrShortWrite, got: %v", err)
   139  				}
   140  			},
   141  		},
   142  		{
   143  			name: "output enclosed by self-described CBOR tag",
   144  			in:   anyObject{},
   145  			assertOnWriter: func() (io.Writer, func(*testing.T)) {
   146  				var b bytes.Buffer
   147  				return &b, func(t *testing.T) {
   148  					if !bytes.HasPrefix(b.Bytes(), []byte{0xd9, 0xd9, 0xf7}) {
   149  						t.Errorf("expected output to have prefix 0xd9d9f7: 0x%x", b.Bytes())
   150  					}
   151  				}
   152  			},
   153  			assertOnError: func(t *testing.T, err error) {
   154  				if err != nil {
   155  					t.Errorf("expected nil error, got: %v", err)
   156  				}
   157  			},
   158  		},
   159  	} {
   160  		t.Run(tc.name, func(t *testing.T) {
   161  			s := NewSerializer(nil, nil)
   162  			w, assertOnWriter := tc.assertOnWriter()
   163  			err := s.Encode(tc.in, w)
   164  			tc.assertOnError(t, err)
   165  			assertOnWriter(t)
   166  		})
   167  	}
   168  }
   169  
   170  func TestDecode(t *testing.T) {
   171  	for _, tc := range []struct {
   172  		name          string
   173  		options       []Option
   174  		data          []byte
   175  		gvk           *schema.GroupVersionKind
   176  		metaFactory   metaFactory
   177  		typer         runtime.ObjectTyper
   178  		creater       runtime.ObjectCreater
   179  		into          runtime.Object
   180  		expectedObj   runtime.Object
   181  		expectedGVK   *schema.GroupVersionKind
   182  		assertOnError func(*testing.T, error)
   183  	}{
   184  		{
   185  			name:        "error determining gvk",
   186  			metaFactory: stubMetaFactory{err: errors.New("test")},
   187  			assertOnError: func(t *testing.T, err error) {
   188  				if err == nil || err.Error() != "test" {
   189  					t.Errorf("expected error \"test\", got: %v", err)
   190  				}
   191  			},
   192  		},
   193  		{
   194  			name:        "typer does not recognize into",
   195  			gvk:         &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
   196  			metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
   197  			typer:       notRegisteredTyper{},
   198  			into:        &anyObject{},
   199  			expectedObj: nil,
   200  			expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
   201  			assertOnError: func(t *testing.T, err error) {
   202  				if !runtime.IsNotRegisteredError(err) {
   203  					t.Errorf("expected NotRegisteredError, got: %v", err)
   204  				}
   205  			},
   206  		},
   207  		{
   208  			name:        "gvk from type of into",
   209  			data:        []byte{0xf6},
   210  			gvk:         &schema.GroupVersionKind{},
   211  			metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
   212  			typer:       stubTyper{gvks: []schema.GroupVersionKind{{Group: "x", Version: "y", Kind: "z"}}},
   213  			into:        &anyObject{},
   214  			expectedObj: &anyObject{},
   215  			expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
   216  			assertOnError: func(t *testing.T, err error) {
   217  				if err != nil {
   218  					t.Errorf("expected nil error, got: %v", err)
   219  				}
   220  			},
   221  		},
   222  		{
   223  			name:        "strict mode strict error",
   224  			options:     []Option{Strict(true)},
   225  			data:        []byte{0xa1, 0x61, 'z', 0x01}, // {'z': 1}
   226  			gvk:         &schema.GroupVersionKind{},
   227  			metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
   228  			typer:       stubTyper{gvks: []schema.GroupVersionKind{{Group: "x", Version: "y", Kind: "z"}}},
   229  			into:        &metav1.PartialObjectMetadata{},
   230  			expectedObj: &metav1.PartialObjectMetadata{},
   231  			expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
   232  			assertOnError: func(t *testing.T, err error) {
   233  				if !runtime.IsStrictDecodingError(err) {
   234  					t.Errorf("expected StrictDecodingError, got: %v", err)
   235  				}
   236  			},
   237  		},
   238  		{
   239  			name:        "no strict mode no strict error",
   240  			data:        []byte{0xa1, 0x61, 'z', 0x01}, // {'z': 1}
   241  			gvk:         &schema.GroupVersionKind{},
   242  			metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
   243  			typer:       stubTyper{gvks: []schema.GroupVersionKind{{Group: "x", Version: "y", Kind: "z"}}},
   244  			into:        &metav1.PartialObjectMetadata{},
   245  			expectedObj: &metav1.PartialObjectMetadata{},
   246  			expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
   247  			assertOnError: func(t *testing.T, err error) {
   248  				if err != nil {
   249  					t.Errorf("expected nil error, got: %v", err)
   250  				}
   251  			},
   252  		},
   253  		{
   254  			name:        "unknown error from typer on into",
   255  			gvk:         &schema.GroupVersionKind{},
   256  			metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
   257  			typer:       stubTyper{err: errors.New("test")},
   258  			into:        &anyObject{},
   259  			expectedObj: nil,
   260  			expectedGVK: &schema.GroupVersionKind{},
   261  			assertOnError: func(t *testing.T, err error) {
   262  				if err == nil || err.Error() != "test" {
   263  					t.Errorf("expected error \"test\", got: %v", err)
   264  				}
   265  			},
   266  		},
   267  		{
   268  			name:        "missing kind",
   269  			gvk:         &schema.GroupVersionKind{Version: "v"},
   270  			metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
   271  			expectedObj: nil,
   272  			expectedGVK: &schema.GroupVersionKind{Version: "v"},
   273  			assertOnError: func(t *testing.T, err error) {
   274  				if !runtime.IsMissingKind(err) {
   275  					t.Errorf("expected MissingKind, got: %v", err)
   276  				}
   277  			},
   278  		},
   279  		{
   280  			name:        "missing version",
   281  			gvk:         &schema.GroupVersionKind{Kind: "k"},
   282  			metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
   283  			expectedObj: nil,
   284  			expectedGVK: &schema.GroupVersionKind{Kind: "k"},
   285  			assertOnError: func(t *testing.T, err error) {
   286  				if !runtime.IsMissingVersion(err) {
   287  					t.Errorf("expected MissingVersion, got: %v", err)
   288  				}
   289  			},
   290  		},
   291  		{
   292  			name:        "creater error",
   293  			gvk:         &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
   294  			metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
   295  			creater:     stubCreater{err: errors.New("test")},
   296  			expectedObj: nil,
   297  			expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
   298  			assertOnError: func(t *testing.T, err error) {
   299  				if err == nil || err.Error() != "test" {
   300  					t.Errorf("expected error \"test\", got: %v", err)
   301  				}
   302  			},
   303  		},
   304  		{
   305  			name:        "unmarshal error",
   306  			data:        nil, // EOF
   307  			gvk:         &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
   308  			metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
   309  			creater:     stubCreater{obj: &anyObject{}},
   310  			expectedObj: nil,
   311  			expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
   312  			assertOnError: func(t *testing.T, err error) {
   313  				if !errors.Is(err, io.EOF) {
   314  					t.Errorf("expected EOF, got: %v", err)
   315  				}
   316  			},
   317  		},
   318  		{
   319  			name:        "strict mode unmarshal error",
   320  			options:     []Option{Strict(true)},
   321  			data:        nil, // EOF
   322  			gvk:         &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
   323  			metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
   324  			creater:     stubCreater{obj: &anyObject{}},
   325  			expectedObj: nil,
   326  			expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
   327  			assertOnError: func(t *testing.T, err error) {
   328  				if !errors.Is(err, io.EOF) {
   329  					t.Errorf("expected EOF, got: %v", err)
   330  				}
   331  			},
   332  		},
   333  		{
   334  			name:        "into unstructured unmarshal error",
   335  			data:        nil, // EOF
   336  			gvk:         &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
   337  			metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
   338  			into:        &unstructured.Unstructured{},
   339  			expectedObj: nil,
   340  			expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
   341  			assertOnError: func(t *testing.T, err error) {
   342  				if !errors.Is(err, io.EOF) {
   343  					t.Errorf("expected EOF, got: %v", err)
   344  				}
   345  			},
   346  		},
   347  		{
   348  			name:        "into unstructured missing kind",
   349  			data:        []byte("\xa1\x6aapiVersion\x61v"),
   350  			into:        &unstructured.Unstructured{},
   351  			expectedObj: nil,
   352  			expectedGVK: &schema.GroupVersionKind{Version: "v"},
   353  			assertOnError: func(t *testing.T, err error) {
   354  				if !runtime.IsMissingKind(err) {
   355  					t.Errorf("expected MissingKind, got: %v", err)
   356  				}
   357  			},
   358  		},
   359  		{
   360  			name:        "into unstructured missing version",
   361  			data:        []byte("\xa1\x64kind\x61k"),
   362  			into:        &unstructured.Unstructured{},
   363  			expectedObj: nil,
   364  			expectedGVK: &schema.GroupVersionKind{Kind: "k"},
   365  			assertOnError: func(t *testing.T, err error) {
   366  				if !runtime.IsMissingVersion(err) {
   367  					t.Errorf("expected MissingVersion, got: %v", err)
   368  				}
   369  			},
   370  		},
   371  		{
   372  			name: "into unstructured",
   373  			data: []byte("\xa2\x6aapiVersion\x61v\x64kind\x61k"),
   374  			into: &unstructured.Unstructured{},
   375  			expectedObj: &unstructured.Unstructured{Object: map[string]interface{}{
   376  				"apiVersion": "v",
   377  				"kind":       "k",
   378  			}},
   379  			expectedGVK: &schema.GroupVersionKind{Version: "v", Kind: "k"},
   380  			assertOnError: func(t *testing.T, err error) {
   381  				if err != nil {
   382  					t.Errorf("expected nil error, got: %v", err)
   383  				}
   384  			},
   385  		},
   386  		{
   387  			name:        "using unstructured creater",
   388  			data:        []byte("\xa2\x6aapiVersion\x61v\x64kind\x61k"),
   389  			metaFactory: &defaultMetaFactory{},
   390  			creater:     stubCreater{obj: &unstructured.Unstructured{}},
   391  			expectedObj: &unstructured.Unstructured{Object: map[string]interface{}{
   392  				"apiVersion": "v",
   393  				"kind":       "k",
   394  			}},
   395  			expectedGVK: &schema.GroupVersionKind{Version: "v", Kind: "k"},
   396  			assertOnError: func(t *testing.T, err error) {
   397  				if err != nil {
   398  					t.Errorf("expected nil error, got: %v", err)
   399  				}
   400  			},
   401  		},
   402  	} {
   403  		t.Run(tc.name, func(t *testing.T) {
   404  			s := newSerializer(tc.metaFactory, tc.creater, tc.typer, tc.options...)
   405  
   406  			actualObj, actualGVK, err := s.Decode(tc.data, tc.gvk, tc.into)
   407  			tc.assertOnError(t, err)
   408  
   409  			if !reflect.DeepEqual(tc.expectedObj, actualObj) {
   410  				t.Error(cmp.Diff(tc.expectedObj, actualObj))
   411  			}
   412  
   413  			if diff := cmp.Diff(tc.expectedGVK, actualGVK); diff != "" {
   414  				t.Error(diff)
   415  			}
   416  		})
   417  	}
   418  }
   419  
   420  func TestMetaFactoryInterpret(t *testing.T) {
   421  	mf := &defaultMetaFactory{}
   422  	_, err := mf.Interpret(nil)
   423  	if err == nil {
   424  		t.Error("expected non-nil error")
   425  	}
   426  	gvk, err := mf.Interpret([]byte("\xa2\x6aapiVersion\x63a/b\x64kind\x61c"))
   427  	if err != nil {
   428  		t.Fatalf("unexpected error: %v", err)
   429  	}
   430  	if diff := cmp.Diff(&schema.GroupVersionKind{Group: "a", Version: "b", Kind: "c"}, gvk); diff != "" {
   431  		t.Error(diff)
   432  	}
   433  }
   434  
   435  type stubTyper struct {
   436  	gvks        []schema.GroupVersionKind
   437  	unversioned bool
   438  	err         error
   439  }
   440  
   441  func (t stubTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
   442  	return t.gvks, t.unversioned, t.err
   443  }
   444  
   445  func (stubTyper) Recognizes(schema.GroupVersionKind) bool {
   446  	return false
   447  }
   448  
   449  type stubCreater struct {
   450  	obj runtime.Object
   451  	err error
   452  }
   453  
   454  func (c stubCreater) New(gvk schema.GroupVersionKind) (runtime.Object, error) {
   455  	return c.obj, c.err
   456  }
   457  
   458  type notRegisteredTyper struct{}
   459  
   460  func (notRegisteredTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
   461  	return nil, false, runtime.NewNotRegisteredErrForType("test", reflect.TypeOf(obj))
   462  }
   463  
   464  func (notRegisteredTyper) Recognizes(schema.GroupVersionKind) bool {
   465  	return false
   466  }
   467  
   468  type stubMetaFactory struct {
   469  	gvk *schema.GroupVersionKind
   470  	err error
   471  }
   472  
   473  func (mf stubMetaFactory) Interpret([]byte) (*schema.GroupVersionKind, error) {
   474  	return mf.gvk, mf.err
   475  }
   476  

View as plain text