k8s.io/client-go@v0.22.2/discovery/cached/memory/memcache_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 memory
    18  
    19  import (
    20  	"errors"
    21  	"net/http"
    22  	"reflect"
    23  	"sync"
    24  	"testing"
    25  
    26  	errorsutil "k8s.io/apimachinery/pkg/api/errors"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/client-go/discovery/fake"
    29  )
    30  
    31  type resourceMapEntry struct {
    32  	list *metav1.APIResourceList
    33  	err  error
    34  }
    35  
    36  type fakeDiscovery struct {
    37  	*fake.FakeDiscovery
    38  
    39  	lock         sync.Mutex
    40  	groupList    *metav1.APIGroupList
    41  	groupListErr error
    42  	resourceMap  map[string]*resourceMapEntry
    43  }
    44  
    45  func (c *fakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) {
    46  	c.lock.Lock()
    47  	defer c.lock.Unlock()
    48  	if rl, ok := c.resourceMap[groupVersion]; ok {
    49  		return rl.list, rl.err
    50  	}
    51  	return nil, errors.New("doesn't exist")
    52  }
    53  
    54  func (c *fakeDiscovery) ServerGroups() (*metav1.APIGroupList, error) {
    55  	c.lock.Lock()
    56  	defer c.lock.Unlock()
    57  	if c.groupList == nil {
    58  		return nil, errors.New("doesn't exist")
    59  	}
    60  	return c.groupList, c.groupListErr
    61  }
    62  
    63  func TestClient(t *testing.T) {
    64  	fake := &fakeDiscovery{
    65  		groupList: &metav1.APIGroupList{
    66  			Groups: []metav1.APIGroup{{
    67  				Name: "astronomy",
    68  				Versions: []metav1.GroupVersionForDiscovery{{
    69  					GroupVersion: "astronomy/v8beta1",
    70  					Version:      "v8beta1",
    71  				}},
    72  			}},
    73  		},
    74  		resourceMap: map[string]*resourceMapEntry{
    75  			"astronomy/v8beta1": {
    76  				list: &metav1.APIResourceList{
    77  					GroupVersion: "astronomy/v8beta1",
    78  					APIResources: []metav1.APIResource{{
    79  						Name:         "dwarfplanets",
    80  						SingularName: "dwarfplanet",
    81  						Namespaced:   true,
    82  						Kind:         "DwarfPlanet",
    83  						ShortNames:   []string{"dp"},
    84  					}},
    85  				},
    86  			},
    87  		},
    88  	}
    89  
    90  	c := NewMemCacheClient(fake)
    91  	if c.Fresh() {
    92  		t.Errorf("Expected not fresh.")
    93  	}
    94  	g, err := c.ServerGroups()
    95  	if err != nil {
    96  		t.Errorf("Unexpected error: %v", err)
    97  	}
    98  	if e, a := fake.groupList, g; !reflect.DeepEqual(e, a) {
    99  		t.Errorf("Expected %#v, got %#v", e, a)
   100  	}
   101  	if !c.Fresh() {
   102  		t.Errorf("Expected fresh.")
   103  	}
   104  	c.Invalidate()
   105  	if c.Fresh() {
   106  		t.Errorf("Expected not fresh.")
   107  	}
   108  
   109  	g, err = c.ServerGroups()
   110  	if err != nil {
   111  		t.Errorf("Unexpected error: %v", err)
   112  	}
   113  	if e, a := fake.groupList, g; !reflect.DeepEqual(e, a) {
   114  		t.Errorf("Expected %#v, got %#v", e, a)
   115  	}
   116  	if !c.Fresh() {
   117  		t.Errorf("Expected fresh.")
   118  	}
   119  	r, err := c.ServerResourcesForGroupVersion("astronomy/v8beta1")
   120  	if err != nil {
   121  		t.Errorf("Unexpected error: %v", err)
   122  	}
   123  	if e, a := fake.resourceMap["astronomy/v8beta1"].list, r; !reflect.DeepEqual(e, a) {
   124  		t.Errorf("Expected %#v, got %#v", e, a)
   125  	}
   126  
   127  	fake.lock.Lock()
   128  	fake.resourceMap = map[string]*resourceMapEntry{
   129  		"astronomy/v8beta1": {
   130  			list: &metav1.APIResourceList{
   131  				GroupVersion: "astronomy/v8beta1",
   132  				APIResources: []metav1.APIResource{{
   133  					Name:         "stars",
   134  					SingularName: "star",
   135  					Namespaced:   true,
   136  					Kind:         "Star",
   137  					ShortNames:   []string{"s"},
   138  				}},
   139  			},
   140  		},
   141  	}
   142  	fake.lock.Unlock()
   143  
   144  	c.Invalidate()
   145  	r, err = c.ServerResourcesForGroupVersion("astronomy/v8beta1")
   146  	if err != nil {
   147  		t.Errorf("Unexpected error: %v", err)
   148  	}
   149  	if e, a := fake.resourceMap["astronomy/v8beta1"].list, r; !reflect.DeepEqual(e, a) {
   150  		t.Errorf("Expected %#v, got %#v", e, a)
   151  	}
   152  }
   153  
   154  func TestServerGroupsFails(t *testing.T) {
   155  	fake := &fakeDiscovery{
   156  		groupList: &metav1.APIGroupList{
   157  			Groups: []metav1.APIGroup{{
   158  				Name: "astronomy",
   159  				Versions: []metav1.GroupVersionForDiscovery{{
   160  					GroupVersion: "astronomy/v8beta1",
   161  					Version:      "v8beta1",
   162  				}},
   163  			}},
   164  		},
   165  		groupListErr: errors.New("some error"),
   166  		resourceMap: map[string]*resourceMapEntry{
   167  			"astronomy/v8beta1": {
   168  				list: &metav1.APIResourceList{
   169  					GroupVersion: "astronomy/v8beta1",
   170  					APIResources: []metav1.APIResource{{
   171  						Name:         "dwarfplanets",
   172  						SingularName: "dwarfplanet",
   173  						Namespaced:   true,
   174  						Kind:         "DwarfPlanet",
   175  						ShortNames:   []string{"dp"},
   176  					}},
   177  				},
   178  			},
   179  		},
   180  	}
   181  
   182  	c := NewMemCacheClient(fake)
   183  	if c.Fresh() {
   184  		t.Errorf("Expected not fresh.")
   185  	}
   186  	_, err := c.ServerGroups()
   187  	if err == nil {
   188  		t.Errorf("Expected error")
   189  	}
   190  	if c.Fresh() {
   191  		t.Errorf("Expected not fresh.")
   192  	}
   193  	fake.lock.Lock()
   194  	fake.groupListErr = nil
   195  	fake.lock.Unlock()
   196  	r, err := c.ServerResourcesForGroupVersion("astronomy/v8beta1")
   197  	if err != nil {
   198  		t.Errorf("Unexpected error: %v", err)
   199  	}
   200  	if e, a := fake.resourceMap["astronomy/v8beta1"].list, r; !reflect.DeepEqual(e, a) {
   201  		t.Errorf("Expected %#v, got %#v", e, a)
   202  	}
   203  	if !c.Fresh() {
   204  		t.Errorf("Expected not fresh.")
   205  	}
   206  }
   207  
   208  func TestPartialPermanentFailure(t *testing.T) {
   209  	fake := &fakeDiscovery{
   210  		groupList: &metav1.APIGroupList{
   211  			Groups: []metav1.APIGroup{
   212  				{
   213  					Name: "astronomy",
   214  					Versions: []metav1.GroupVersionForDiscovery{{
   215  						GroupVersion: "astronomy/v8beta1",
   216  						Version:      "v8beta1",
   217  					}},
   218  				},
   219  				{
   220  					Name: "astronomy2",
   221  					Versions: []metav1.GroupVersionForDiscovery{{
   222  						GroupVersion: "astronomy2/v8beta1",
   223  						Version:      "v8beta1",
   224  					}},
   225  				},
   226  			},
   227  		},
   228  		resourceMap: map[string]*resourceMapEntry{
   229  			"astronomy/v8beta1": {
   230  				err: errors.New("some permanent error"),
   231  			},
   232  			"astronomy2/v8beta1": {
   233  				list: &metav1.APIResourceList{
   234  					GroupVersion: "astronomy2/v8beta1",
   235  					APIResources: []metav1.APIResource{{
   236  						Name:         "dwarfplanets",
   237  						SingularName: "dwarfplanet",
   238  						Namespaced:   true,
   239  						Kind:         "DwarfPlanet",
   240  						ShortNames:   []string{"dp"},
   241  					}},
   242  				},
   243  			},
   244  		},
   245  	}
   246  
   247  	c := NewMemCacheClient(fake)
   248  	if c.Fresh() {
   249  		t.Errorf("Expected not fresh.")
   250  	}
   251  	r, err := c.ServerResourcesForGroupVersion("astronomy2/v8beta1")
   252  	if err != nil {
   253  		t.Errorf("Unexpected error: %v", err)
   254  	}
   255  	if e, a := fake.resourceMap["astronomy2/v8beta1"].list, r; !reflect.DeepEqual(e, a) {
   256  		t.Errorf("Expected %#v, got %#v", e, a)
   257  	}
   258  	_, err = c.ServerResourcesForGroupVersion("astronomy/v8beta1")
   259  	if err == nil {
   260  		t.Errorf("Expected error, got nil")
   261  	}
   262  
   263  	fake.lock.Lock()
   264  	fake.resourceMap["astronomy/v8beta1"] = &resourceMapEntry{
   265  		list: &metav1.APIResourceList{
   266  			GroupVersion: "astronomy/v8beta1",
   267  			APIResources: []metav1.APIResource{{
   268  				Name:         "dwarfplanets",
   269  				SingularName: "dwarfplanet",
   270  				Namespaced:   true,
   271  				Kind:         "DwarfPlanet",
   272  				ShortNames:   []string{"dp"},
   273  			}},
   274  		},
   275  		err: nil,
   276  	}
   277  	fake.lock.Unlock()
   278  	// We don't retry permanent errors, so it should fail.
   279  	_, err = c.ServerResourcesForGroupVersion("astronomy/v8beta1")
   280  	if err == nil {
   281  		t.Errorf("Expected error, got nil")
   282  	}
   283  	c.Invalidate()
   284  
   285  	// After Invalidate, we should retry.
   286  	r, err = c.ServerResourcesForGroupVersion("astronomy/v8beta1")
   287  	if err != nil {
   288  		t.Errorf("Unexpected error: %v", err)
   289  	}
   290  	if e, a := fake.resourceMap["astronomy/v8beta1"].list, r; !reflect.DeepEqual(e, a) {
   291  		t.Errorf("Expected %#v, got %#v", e, a)
   292  	}
   293  }
   294  
   295  func TestPartialRetryableFailure(t *testing.T) {
   296  	fake := &fakeDiscovery{
   297  		groupList: &metav1.APIGroupList{
   298  			Groups: []metav1.APIGroup{
   299  				{
   300  					Name: "astronomy",
   301  					Versions: []metav1.GroupVersionForDiscovery{{
   302  						GroupVersion: "astronomy/v8beta1",
   303  						Version:      "v8beta1",
   304  					}},
   305  				},
   306  				{
   307  					Name: "astronomy2",
   308  					Versions: []metav1.GroupVersionForDiscovery{{
   309  						GroupVersion: "astronomy2/v8beta1",
   310  						Version:      "v8beta1",
   311  					}},
   312  				},
   313  			},
   314  		},
   315  		resourceMap: map[string]*resourceMapEntry{
   316  			"astronomy/v8beta1": {
   317  				err: &errorsutil.StatusError{
   318  					ErrStatus: metav1.Status{
   319  						Message: "Some retryable error",
   320  						Code:    int32(http.StatusServiceUnavailable),
   321  						Reason:  metav1.StatusReasonServiceUnavailable,
   322  					},
   323  				},
   324  			},
   325  			"astronomy2/v8beta1": {
   326  				list: &metav1.APIResourceList{
   327  					GroupVersion: "astronomy2/v8beta1",
   328  					APIResources: []metav1.APIResource{{
   329  						Name:         "dwarfplanets",
   330  						SingularName: "dwarfplanet",
   331  						Namespaced:   true,
   332  						Kind:         "DwarfPlanet",
   333  						ShortNames:   []string{"dp"},
   334  					}},
   335  				},
   336  			},
   337  		},
   338  	}
   339  
   340  	c := NewMemCacheClient(fake)
   341  	if c.Fresh() {
   342  		t.Errorf("Expected not fresh.")
   343  	}
   344  	r, err := c.ServerResourcesForGroupVersion("astronomy2/v8beta1")
   345  	if err != nil {
   346  		t.Errorf("Unexpected error: %v", err)
   347  	}
   348  	if e, a := fake.resourceMap["astronomy2/v8beta1"].list, r; !reflect.DeepEqual(e, a) {
   349  		t.Errorf("Expected %#v, got %#v", e, a)
   350  	}
   351  	_, err = c.ServerResourcesForGroupVersion("astronomy/v8beta1")
   352  	if err == nil {
   353  		t.Errorf("Expected error, got nil")
   354  	}
   355  
   356  	fake.lock.Lock()
   357  	fake.resourceMap["astronomy/v8beta1"] = &resourceMapEntry{
   358  		list: &metav1.APIResourceList{
   359  			GroupVersion: "astronomy/v8beta1",
   360  			APIResources: []metav1.APIResource{{
   361  				Name:         "dwarfplanets",
   362  				SingularName: "dwarfplanet",
   363  				Namespaced:   true,
   364  				Kind:         "DwarfPlanet",
   365  				ShortNames:   []string{"dp"},
   366  			}},
   367  		},
   368  		err: nil,
   369  	}
   370  	fake.lock.Unlock()
   371  	// We should retry retryable error even without Invalidate() being called,
   372  	// so no error is expected.
   373  	r, err = c.ServerResourcesForGroupVersion("astronomy/v8beta1")
   374  	if err != nil {
   375  		t.Errorf("Expected no error, got %v", err)
   376  	}
   377  	if e, a := fake.resourceMap["astronomy/v8beta1"].list, r; !reflect.DeepEqual(e, a) {
   378  		t.Errorf("Expected %#v, got %#v", e, a)
   379  	}
   380  
   381  	// Check that the last result was cached and we don't retry further.
   382  	fake.lock.Lock()
   383  	fake.resourceMap["astronomy/v8beta1"].err = errors.New("some permanent error")
   384  	fake.lock.Unlock()
   385  	r, err = c.ServerResourcesForGroupVersion("astronomy/v8beta1")
   386  	if err != nil {
   387  		t.Errorf("Expected no error, got %v", err)
   388  	}
   389  	if e, a := fake.resourceMap["astronomy/v8beta1"].list, r; !reflect.DeepEqual(e, a) {
   390  		t.Errorf("Expected %#v, got %#v", e, a)
   391  	}
   392  }