k8s.io/apiserver@v0.31.1/pkg/storage/cacher/caching_object_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 cacher
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"io"
    23  	"sync"
    24  	"sync/atomic"
    25  	"testing"
    26  
    27  	v1 "k8s.io/api/core/v1"
    28  	"k8s.io/apimachinery/pkg/api/meta"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  )
    32  
    33  type mockEncoder struct {
    34  	identifier     runtime.Identifier
    35  	expectedResult string
    36  	expectedError  error
    37  
    38  	callsNumber int32
    39  }
    40  
    41  func newMockEncoder(id, result string, err error) *mockEncoder {
    42  	return &mockEncoder{
    43  		identifier:     runtime.Identifier(id),
    44  		expectedResult: result,
    45  		expectedError:  err,
    46  	}
    47  }
    48  
    49  func (e *mockEncoder) encode(_ runtime.Object, w io.Writer) error {
    50  	atomic.AddInt32(&e.callsNumber, 1)
    51  	if e.expectedError != nil {
    52  		return e.expectedError
    53  	}
    54  	_, err := w.Write([]byte(e.expectedResult))
    55  	return err
    56  }
    57  
    58  func TestCachingObject(t *testing.T) {
    59  	object, err := newCachingObject(&v1.Pod{})
    60  	if err != nil {
    61  		t.Fatalf("couldn't create cachingObject: %v", err)
    62  	}
    63  
    64  	encoders := []*mockEncoder{
    65  		newMockEncoder("1", "result1", nil),
    66  		newMockEncoder("2", "", fmt.Errorf("mock error")),
    67  		newMockEncoder("3", "result3", nil),
    68  	}
    69  
    70  	for _, encoder := range encoders {
    71  		buffer := bytes.NewBuffer(nil)
    72  		err := object.CacheEncode(encoder.identifier, encoder.encode, buffer)
    73  		if a, e := err, encoder.expectedError; e != a {
    74  			t.Errorf("%s: unexpected error: %v, expected: %v", encoder.identifier, a, e)
    75  		}
    76  		if a, e := buffer.String(), encoder.expectedResult; e != a {
    77  			t.Errorf("%s: unexpected result: %s, expected: %s", encoder.identifier, a, e)
    78  		}
    79  	}
    80  	for _, encoder := range encoders {
    81  		if encoder.callsNumber != 1 {
    82  			t.Errorf("%s: unexpected number of encode() calls: %d", encoder.identifier, encoder.callsNumber)
    83  		}
    84  	}
    85  }
    86  
    87  func TestCachingObjectFieldAccessors(t *testing.T) {
    88  	object, err := newCachingObject(&v1.Pod{})
    89  	if err != nil {
    90  		t.Fatalf("couldn't create cachingObject: %v", err)
    91  	}
    92  
    93  	// Given accessors for all fields implement the same logic,
    94  	// we are choosing an arbitrary one to test.
    95  	namespace := "namespace"
    96  	object.SetNamespace(namespace)
    97  
    98  	encodeNamespace := func(obj runtime.Object, w io.Writer) error {
    99  		accessor, err := meta.Accessor(obj)
   100  		if err != nil {
   101  			t.Fatalf("failed to get accessor for %#v: %v", obj, err)
   102  		}
   103  		_, err = w.Write([]byte(accessor.GetNamespace()))
   104  		return err
   105  	}
   106  	buffer := bytes.NewBuffer(nil)
   107  	if err := object.CacheEncode("", encodeNamespace, buffer); err != nil {
   108  		t.Errorf("unexpected error: %v", err)
   109  	}
   110  	if a, e := buffer.String(), namespace; a != e {
   111  		t.Errorf("unexpected serialization: %s, expected: %s", a, e)
   112  	}
   113  
   114  	// GetObject should also set namespace.
   115  	buffer.Reset()
   116  	if err := encodeNamespace(object.GetObject(), buffer); err != nil {
   117  		t.Errorf("unexpected error: %v", err)
   118  	}
   119  	if a, e := buffer.String(), namespace; a != e {
   120  		t.Errorf("unexpected serialization: %s, expected: %s", a, e)
   121  	}
   122  }
   123  
   124  func TestCachingObjectRaces(t *testing.T) {
   125  	object, err := newCachingObject(&v1.Pod{})
   126  	if err != nil {
   127  		t.Fatalf("couldn't create cachingObject: %v", err)
   128  	}
   129  
   130  	encoders := []*mockEncoder{}
   131  	for i := 0; i < 10; i++ {
   132  		encoder := newMockEncoder(fmt.Sprintf("%d", i), "result", nil)
   133  		encoders = append(encoders, encoder)
   134  	}
   135  
   136  	numWorkers := 1000
   137  	wg := &sync.WaitGroup{}
   138  	wg.Add(numWorkers)
   139  
   140  	for i := 0; i < numWorkers; i++ {
   141  		go func() {
   142  			defer wg.Done()
   143  			object.SetNamespace("namespace")
   144  			buffer := bytes.NewBuffer(nil)
   145  			for _, encoder := range encoders {
   146  				buffer.Reset()
   147  				if err := object.CacheEncode(encoder.identifier, encoder.encode, buffer); err != nil {
   148  					t.Errorf("unexpected error: %v", err)
   149  				}
   150  				if callsNumber := atomic.LoadInt32(&encoder.callsNumber); callsNumber != 1 {
   151  					t.Errorf("unexpected number of serializations: %d", callsNumber)
   152  				}
   153  			}
   154  			accessor, err := meta.Accessor(object.GetObject())
   155  			if err != nil {
   156  				t.Errorf("failed to get accessor: %v", err)
   157  				return
   158  			}
   159  			if namespace := accessor.GetNamespace(); namespace != "namespace" {
   160  				t.Errorf("unexpected namespace: %s", namespace)
   161  			}
   162  		}()
   163  	}
   164  	wg.Wait()
   165  }
   166  
   167  func TestCachingObjectLazyDeepCopy(t *testing.T) {
   168  	pod := &v1.Pod{
   169  		ObjectMeta: metav1.ObjectMeta{
   170  			Name:            "name",
   171  			ResourceVersion: "123",
   172  		},
   173  	}
   174  	object, err := newCachingObject(pod)
   175  	if err != nil {
   176  		t.Fatalf("couldn't create cachingObject: %v", err)
   177  	}
   178  
   179  	if object.deepCopied != false {
   180  		t.Errorf("object deep-copied without the need")
   181  	}
   182  
   183  	object.SetResourceVersion("123")
   184  	if object.deepCopied != false {
   185  		t.Errorf("object deep-copied on no-op change")
   186  	}
   187  	object.SetResourceVersion("234")
   188  	if object.deepCopied != true {
   189  		t.Errorf("object not deep-copied on change")
   190  	}
   191  }