k8s.io/apiserver@v0.29.3/pkg/storage/cacher/watch_cache_interval_test.go (about)

     1  /*
     2  Copyright 2021 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  	"errors"
    21  	"fmt"
    22  	"reflect"
    23  	"sync"
    24  	"testing"
    25  
    26  	v1 "k8s.io/api/core/v1"
    27  	"k8s.io/apimachinery/pkg/fields"
    28  	"k8s.io/apimachinery/pkg/labels"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  	"k8s.io/apimachinery/pkg/watch"
    31  	"k8s.io/client-go/tools/cache"
    32  )
    33  
    34  func intervalFromEvents(events []*watchCacheEvent) *watchCacheInterval {
    35  	startIndex, endIndex, locker := 0, len(events), &sync.Mutex{}
    36  	indexer := func(i int) *watchCacheEvent {
    37  		if len(events) == 0 {
    38  			return nil
    39  		}
    40  		return events[i]
    41  	}
    42  	indexValidator := func(_ int) bool { return true }
    43  
    44  	return newCacheInterval(startIndex, endIndex, indexer, indexValidator, locker)
    45  }
    46  
    47  func bufferFromEvents(events []*watchCacheEvent) *watchCacheIntervalBuffer {
    48  	wcib := &watchCacheIntervalBuffer{
    49  		buffer:     make([]*watchCacheEvent, bufferSize),
    50  		startIndex: 0,
    51  		endIndex:   len(events),
    52  	}
    53  	copy(wcib.buffer, events)
    54  
    55  	return wcib
    56  }
    57  
    58  func generateEvents(start, end int) []*watchCacheEvent {
    59  	n := end - start
    60  	events := make([]*watchCacheEvent, n)
    61  	for i := 0; i < n; i++ {
    62  		events[i] = &watchCacheEvent{
    63  			Type:   watch.Added,
    64  			Object: makeTestPod(fmt.Sprintf("pod%d", start+i), uint64(start+i)),
    65  		}
    66  	}
    67  	return events
    68  }
    69  
    70  func verifyEvent(ok bool, event, expectedEvent *watchCacheEvent) error {
    71  	if !ok {
    72  		return fmt.Errorf("expected event: %#v, got no event", expectedEvent)
    73  	}
    74  
    75  	if event == nil {
    76  		return fmt.Errorf("unexpected nil event, expected: %#v", expectedEvent)
    77  	}
    78  
    79  	if !reflect.DeepEqual(event, expectedEvent) {
    80  		return fmt.Errorf("expected %v, got %v", *event, *expectedEvent)
    81  	}
    82  
    83  	return nil
    84  }
    85  
    86  func verifyNoEvent(ok bool, event *watchCacheEvent) error {
    87  	if ok {
    88  		return errors.New("unexpected bool value indicating buffer is not empty")
    89  	}
    90  	if event != nil {
    91  		return fmt.Errorf("unexpected event received, expected: nil, got %v", *event)
    92  	}
    93  
    94  	return nil
    95  }
    96  
    97  func TestIntervalBufferIsFull(t *testing.T) {
    98  	cases := []struct {
    99  		endIndex int
   100  		expected bool
   101  	}{
   102  		{endIndex: bufferSize - 1, expected: false},
   103  		{endIndex: bufferSize, expected: true},
   104  		{endIndex: bufferSize + 1, expected: true},
   105  	}
   106  
   107  	for _, c := range cases {
   108  		wcib := &watchCacheIntervalBuffer{endIndex: c.endIndex}
   109  		actual := wcib.isFull()
   110  		if actual != c.expected {
   111  			t.Errorf("expected %v, got %v", c.expected, actual)
   112  		}
   113  	}
   114  }
   115  
   116  func TestIntervalBufferIsEmpty(t *testing.T) {
   117  	cases := []struct {
   118  		startIndex int
   119  		endIndex   int
   120  		expected   bool
   121  	}{
   122  		{startIndex: 0, endIndex: 10, expected: false},
   123  		{startIndex: 5, endIndex: 20, expected: false},
   124  		{startIndex: 50, endIndex: 50, expected: true},
   125  	}
   126  
   127  	for _, c := range cases {
   128  		wcib := &watchCacheIntervalBuffer{
   129  			startIndex: c.startIndex,
   130  			endIndex:   c.endIndex,
   131  		}
   132  		actual := wcib.isEmpty()
   133  		if actual != c.expected {
   134  			t.Errorf("expected %v, got %v", c.expected, actual)
   135  		}
   136  	}
   137  }
   138  
   139  func TestIntervalBufferNext(t *testing.T) {
   140  	cases := []struct {
   141  		name   string
   142  		events []*watchCacheEvent
   143  	}{
   144  		{
   145  			name: "buffer has elements",
   146  			events: []*watchCacheEvent{
   147  				{Type: watch.Added, Object: makeTestPod("pod1", 1)},
   148  				{Type: watch.Added, Object: makeTestPod("pod2", 2)},
   149  				{Type: watch.Modified, Object: makeTestPod("pod3", 3)},
   150  			},
   151  		},
   152  		{
   153  			name:   "buffer is empty",
   154  			events: []*watchCacheEvent{},
   155  		},
   156  	}
   157  
   158  	for _, c := range cases {
   159  		t.Run(c.name, func(t *testing.T) {
   160  			wcib := bufferFromEvents(c.events)
   161  			for i := 0; i < len(c.events); i++ {
   162  				event, ok := wcib.next()
   163  				if err := verifyEvent(ok, event, c.events[i]); err != nil {
   164  					t.Error(err)
   165  				}
   166  			}
   167  			event, ok := wcib.next()
   168  			if err := verifyNoEvent(ok, event); err != nil {
   169  				t.Error(err)
   170  			}
   171  		})
   172  	}
   173  }
   174  
   175  func TestFillBuffer(t *testing.T) {
   176  	cases := []struct {
   177  		name            string
   178  		numEventsToFill int
   179  	}{
   180  		{
   181  			name:            "no events to put in buffer",
   182  			numEventsToFill: 0,
   183  		},
   184  		{
   185  			name:            "less than bufferSize events to put in buffer",
   186  			numEventsToFill: 5,
   187  		},
   188  		{
   189  			name:            "equal to bufferSize events to put in buffer",
   190  			numEventsToFill: bufferSize,
   191  		},
   192  		{
   193  			name:            "greater than bufferSize events to put in buffer",
   194  			numEventsToFill: bufferSize + 5,
   195  		},
   196  	}
   197  
   198  	for _, c := range cases {
   199  		t.Run(c.name, func(t *testing.T) {
   200  			events := generateEvents(0, c.numEventsToFill)
   201  			wci := intervalFromEvents(events)
   202  
   203  			for i := 0; i < len(events); i++ {
   204  				if i%bufferSize == 0 {
   205  					wci.fillBuffer()
   206  				}
   207  				event, ok := wci.buffer.next()
   208  				if err := verifyEvent(ok, event, events[i]); err != nil {
   209  					t.Error(err)
   210  				}
   211  				// If we have already received bufferSize number of events,
   212  				// buffer should be empty and we should receive no event.
   213  				if i%bufferSize == bufferSize-1 {
   214  					event, ok := wci.buffer.next()
   215  					if err := verifyNoEvent(ok, event); err != nil {
   216  						t.Error(err)
   217  					}
   218  				}
   219  			}
   220  			// buffer should be empty and return no event.
   221  			event, ok := wci.buffer.next()
   222  			if err := verifyNoEvent(ok, event); err != nil {
   223  				t.Error(err)
   224  			}
   225  			// Buffer should be empty now, an additional fillBuffer()
   226  			// should make no difference.
   227  			wci.fillBuffer()
   228  			event, ok = wci.buffer.next()
   229  			if err := verifyNoEvent(ok, event); err != nil {
   230  				t.Error(err)
   231  			}
   232  		})
   233  	}
   234  }
   235  
   236  func TestCacheIntervalNextFromWatchCache(t *testing.T) {
   237  	// Have the capacity such that it facilitates
   238  	// filling the interval buffer more than once
   239  	// completely and then some more - 10 here is
   240  	// arbitrary.
   241  	const capacity = 2*bufferSize + 10
   242  
   243  	cases := []struct {
   244  		name string
   245  		// The total number of events that the watch
   246  		// cache will be populated with to start with.
   247  		eventsAddedToWatchcache int
   248  		intervalStartIndex      int
   249  	}{
   250  		{
   251  			name:                    "watchCache empty, eventsAddedToWatchcache = 0",
   252  			eventsAddedToWatchcache: 0,
   253  			intervalStartIndex:      0,
   254  		},
   255  		{
   256  			name:                    "watchCache partially propagated, eventsAddedToWatchcache < capacity",
   257  			eventsAddedToWatchcache: bufferSize,
   258  			intervalStartIndex:      0,
   259  		},
   260  		{
   261  			name:                    "watchCache partially propagated, eventsAddedToWatchcache < capacity, intervalStartIndex at some offset",
   262  			eventsAddedToWatchcache: bufferSize,
   263  			intervalStartIndex:      5,
   264  		},
   265  		{
   266  			name:                    "watchCache fully propagated, eventsAddedToWatchcache = capacity",
   267  			eventsAddedToWatchcache: capacity,
   268  			intervalStartIndex:      0,
   269  		},
   270  		{
   271  			name:                    "watchCache fully propagated, eventsAddedToWatchcache = capacity, intervalStartIndex at some offset",
   272  			eventsAddedToWatchcache: capacity,
   273  			intervalStartIndex:      5,
   274  		},
   275  		{
   276  			name:                    "watchCache over propagated, eventsAddedToWatchcache > capacity",
   277  			eventsAddedToWatchcache: capacity + bufferSize,
   278  			intervalStartIndex:      0,
   279  		},
   280  		{
   281  			name:                    "watchCache over propagated, eventsAddedToWatchcache > capacity, intervalStartIndex at some offset",
   282  			eventsAddedToWatchcache: capacity + bufferSize,
   283  			intervalStartIndex:      5,
   284  		},
   285  	}
   286  
   287  	for _, c := range cases {
   288  		t.Run(c.name, func(t *testing.T) {
   289  			wc := newTestWatchCache(capacity, &cache.Indexers{})
   290  			defer wc.Stop()
   291  			for i := 0; i < c.eventsAddedToWatchcache; i++ {
   292  				wc.Add(makeTestPod(fmt.Sprintf("pod%d", i), uint64(i)))
   293  			}
   294  			indexerFunc := func(i int) *watchCacheEvent {
   295  				return wc.cache[i%wc.capacity]
   296  			}
   297  
   298  			wci := newCacheInterval(
   299  				c.intervalStartIndex,
   300  				wc.endIndex,
   301  				indexerFunc,
   302  				wc.isIndexValidLocked,
   303  				&wc.RWMutex,
   304  			)
   305  
   306  			numExpectedEvents := wc.endIndex - c.intervalStartIndex
   307  			for i := 0; i < numExpectedEvents; i++ {
   308  				// Simulate and test interval invalidation iff
   309  				// the watchCache itself is not empty.
   310  				if c.eventsAddedToWatchcache > 0 {
   311  					// The points at which we want to artificially
   312  					// invalidate the interval and test its behaviour
   313  					// should be multiples of bufferSize. This is because
   314  					// invalidation only needs to be checked when we are
   315  					// copying over events from the underlying watch cache,
   316  					// i.e. freshly filling in the interval buffer.
   317  					if i%bufferSize == 0 && i != c.eventsAddedToWatchcache {
   318  						originalCacheStartIndex := wc.startIndex
   319  						wc.startIndex = wci.startIndex + 1
   320  						event, err := wci.Next()
   321  						if err == nil {
   322  							t.Errorf("expected non-nil error")
   323  						}
   324  						if event != nil {
   325  							t.Errorf("expected nil event, got %v", *event)
   326  						}
   327  						// Restore startIndex.
   328  						wc.startIndex = originalCacheStartIndex
   329  					}
   330  				}
   331  
   332  				// Check if the state of the interval buffer is as expected.
   333  				// The interval buffer can be empty either when received is
   334  				// either a multiple of bufferSize (after one complete fill)
   335  				// or when received is equal to the number of expected events.
   336  				// The latter happens when partial filling occurs and no more
   337  				// events are left post the partial fill.
   338  				if wci.buffer.isEmpty() != (i%bufferSize == 0 || i == numExpectedEvents) {
   339  					t.Error("expected empty interval buffer")
   340  					return
   341  				}
   342  
   343  				event, err := wci.Next()
   344  				if err != nil {
   345  					t.Errorf("unexpected error: %v", err)
   346  					return
   347  				}
   348  
   349  				expectedIndex := (c.intervalStartIndex + i) % wc.capacity
   350  				expectedEvent := wc.cache[expectedIndex]
   351  				if err := verifyEvent(true, event, expectedEvent); err != nil {
   352  					t.Error(err)
   353  				}
   354  			}
   355  			event, err := wci.Next()
   356  			ok := err != nil
   357  			if err := verifyNoEvent(ok, event); err != nil {
   358  				t.Error(err)
   359  			}
   360  		})
   361  	}
   362  }
   363  
   364  func TestCacheIntervalNextFromStore(t *testing.T) {
   365  	getAttrsFunc := func(obj runtime.Object) (labels.Set, fields.Set, error) {
   366  		pod, ok := obj.(*v1.Pod)
   367  		if !ok {
   368  			return nil, nil, fmt.Errorf("not a pod")
   369  		}
   370  		return labels.Set(pod.Labels), fields.Set{"spec.nodeName": pod.Spec.NodeName}, nil
   371  	}
   372  	const numEvents = 50
   373  	store := cache.NewIndexer(storeElementKey, storeElementIndexers(nil))
   374  	events := make(map[string]*watchCacheEvent)
   375  	var rv uint64 = 1 // arbitrary number; rv till which the watch cache has progressed.
   376  
   377  	for i := 0; i < numEvents; i++ {
   378  		elem := makeTestStoreElement(makeTestPod(fmt.Sprintf("pod%d", i), uint64(i)))
   379  		objLabels, objFields, err := getAttrsFunc(elem.Object)
   380  		if err != nil {
   381  			t.Fatal(err)
   382  		}
   383  		events[elem.Key] = &watchCacheEvent{
   384  			Type:            watch.Added,
   385  			Object:          elem.Object,
   386  			ObjLabels:       objLabels,
   387  			ObjFields:       objFields,
   388  			Key:             elem.Key,
   389  			ResourceVersion: rv,
   390  		}
   391  		store.Add(elem)
   392  	}
   393  
   394  	wci, err := newCacheIntervalFromStore(rv, store, getAttrsFunc)
   395  	if err != nil {
   396  		t.Fatal(err)
   397  	}
   398  
   399  	for i := 0; i < numEvents; i++ {
   400  		// The interval buffer can never be empty unless
   401  		// all elements obtained through List() have been
   402  		// returned.
   403  		if wci.buffer.isEmpty() && i != numEvents {
   404  			t.Fatal("expected non-empty interval buffer")
   405  		}
   406  		event, err := wci.Next()
   407  		if err != nil {
   408  			t.Fatalf("unexpected error: %v", err)
   409  		}
   410  		if event == nil {
   411  			t.Error("unexpected nil event")
   412  			break
   413  		}
   414  		expectedEvent, ok := events[event.Key]
   415  		if !ok {
   416  			t.Fatalf("event with key %s not found", event.Key)
   417  		}
   418  		if !reflect.DeepEqual(event, expectedEvent) {
   419  			t.Errorf("expected: %v, got %v", *events[event.Key], *event)
   420  		}
   421  	}
   422  
   423  	// The interval's buffer should now be empty.
   424  	if !wci.buffer.isEmpty() {
   425  		t.Error("expected cache interval's buffer to be empty")
   426  	}
   427  }