...

Source file src/oras.land/oras-go/pkg/registry/remote/auth/cache_test.go

Documentation: oras.land/oras-go/pkg/registry/remote/auth

     1  /*
     2  Copyright The ORAS Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  package auth
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"strconv"
    21  	"sync"
    22  	"sync/atomic"
    23  	"testing"
    24  	"time"
    25  
    26  	errdef "oras.land/oras-go/pkg/content"
    27  )
    28  
    29  func Test_concurrentCache_GetScheme(t *testing.T) {
    30  	cache := NewCache()
    31  
    32  	// no entry in the cache
    33  	ctx := context.Background()
    34  	registry := "localhost:5000"
    35  	got, err := cache.GetScheme(ctx, registry)
    36  	if want := errdef.ErrNotFound; err != want {
    37  		t.Fatalf("concurrentCache.GetScheme() error = %v, wantErr %v", err, want)
    38  	}
    39  	if got != SchemeUnknown {
    40  		t.Errorf("concurrentCache.GetScheme() = %v, want %v", got, SchemeUnknown)
    41  	}
    42  
    43  	// set an cache entry
    44  	scheme := SchemeBasic
    45  	_, err = cache.Set(ctx, registry, scheme, "", func(c context.Context) (string, error) {
    46  		return "foo", nil
    47  	})
    48  	if err != nil {
    49  		t.Fatalf("failed to set cache: %v", err)
    50  	}
    51  
    52  	// verify cache
    53  	got, err = cache.GetScheme(ctx, registry)
    54  	if err != nil {
    55  		t.Fatalf("concurrentCache.GetScheme() error = %v", err)
    56  	}
    57  	if got != scheme {
    58  		t.Errorf("concurrentCache.GetScheme() = %v, want %v", got, scheme)
    59  	}
    60  
    61  	// set cache entry again
    62  	scheme = SchemeBearer
    63  	_, err = cache.Set(ctx, registry, scheme, "", func(c context.Context) (string, error) {
    64  		return "bar", nil
    65  	})
    66  	if err != nil {
    67  		t.Fatalf("failed to set cache: %v", err)
    68  	}
    69  
    70  	// verify cache
    71  	got, err = cache.GetScheme(ctx, registry)
    72  	if err != nil {
    73  		t.Fatalf("concurrentCache.GetScheme() error = %v", err)
    74  	}
    75  	if got != scheme {
    76  		t.Errorf("concurrentCache.GetScheme() = %v, want %v", got, scheme)
    77  	}
    78  
    79  	// test other registry
    80  	registry = "localhost:5001"
    81  	got, err = cache.GetScheme(ctx, registry)
    82  	if want := errdef.ErrNotFound; err != want {
    83  		t.Fatalf("concurrentCache.GetScheme() error = %v, wantErr %v", err, want)
    84  	}
    85  	if got != SchemeUnknown {
    86  		t.Errorf("concurrentCache.GetScheme() = %v, want %v", got, SchemeUnknown)
    87  	}
    88  }
    89  
    90  func Test_concurrentCache_GetToken(t *testing.T) {
    91  	cache := NewCache()
    92  
    93  	// no entry in the cache
    94  	ctx := context.Background()
    95  	registry := "localhost:5000"
    96  	scheme := SchemeBearer
    97  	key := "1st key"
    98  	got, err := cache.GetToken(ctx, registry, scheme, key)
    99  	if want := errdef.ErrNotFound; err != want {
   100  		t.Fatalf("concurrentCache.GetToken() error = %v, wantErr %v", err, want)
   101  	}
   102  	if got != "" {
   103  		t.Errorf("concurrentCache.GetToken() = %v, want %v", got, "")
   104  	}
   105  
   106  	// set an cache entry
   107  	_, err = cache.Set(ctx, registry, scheme, key, func(c context.Context) (string, error) {
   108  		return "foo", nil
   109  	})
   110  	if err != nil {
   111  		t.Fatalf("failed to set cache: %v", err)
   112  	}
   113  
   114  	// verify cache
   115  	got, err = cache.GetToken(ctx, registry, scheme, key)
   116  	if err != nil {
   117  		t.Fatalf("concurrentCache.GetToken() error = %v", err)
   118  	}
   119  	if want := "foo"; got != want {
   120  		t.Errorf("concurrentCache.GetToken() = %v, want %v", got, want)
   121  	}
   122  
   123  	// set cache entry again
   124  	_, err = cache.Set(ctx, registry, scheme, key, func(c context.Context) (string, error) {
   125  		return "bar", nil
   126  	})
   127  	if err != nil {
   128  		t.Fatalf("failed to set cache: %v", err)
   129  	}
   130  
   131  	// verify cache
   132  	got, err = cache.GetToken(ctx, registry, scheme, key)
   133  	if err != nil {
   134  		t.Fatalf("concurrentCache.GetToken() error = %v", err)
   135  	}
   136  	if want := "bar"; got != want {
   137  		t.Errorf("concurrentCache.GetToken() = %v, want %v", got, want)
   138  	}
   139  
   140  	// test other key
   141  	key = "2nd key"
   142  	got, err = cache.GetToken(ctx, registry, scheme, key)
   143  	if want := errdef.ErrNotFound; err != want {
   144  		t.Fatalf("concurrentCache.GetToken() error = %v, wantErr %v", err, want)
   145  	}
   146  	if got != "" {
   147  		t.Errorf("concurrentCache.GetToken() = %v, want %v", got, "")
   148  	}
   149  
   150  	// set an cache entry
   151  	_, err = cache.Set(ctx, registry, scheme, key, func(c context.Context) (string, error) {
   152  		return "hello world", nil
   153  	})
   154  	if err != nil {
   155  		t.Fatalf("failed to set cache: %v", err)
   156  	}
   157  
   158  	// verify cache
   159  	got, err = cache.GetToken(ctx, registry, scheme, key)
   160  	if err != nil {
   161  		t.Fatalf("concurrentCache.GetToken() error = %v", err)
   162  	}
   163  	if want := "hello world"; got != want {
   164  		t.Errorf("concurrentCache.GetToken() = %v, want %v", got, want)
   165  	}
   166  
   167  	// verify cache of the previous key as keys should not interference each
   168  	// other
   169  	key = "1st key"
   170  	got, err = cache.GetToken(ctx, registry, scheme, key)
   171  	if err != nil {
   172  		t.Fatalf("concurrentCache.GetToken() error = %v", err)
   173  	}
   174  	if want := "bar"; got != want {
   175  		t.Errorf("concurrentCache.GetToken() = %v, want %v", got, want)
   176  	}
   177  
   178  	// test other registry
   179  	registry = "localhost:5001"
   180  	got, err = cache.GetToken(ctx, registry, scheme, key)
   181  	if want := errdef.ErrNotFound; err != want {
   182  		t.Fatalf("concurrentCache.GetToken() error = %v, wantErr %v", err, want)
   183  	}
   184  	if got != "" {
   185  		t.Errorf("concurrentCache.GetToken() = %v, want %v", got, "")
   186  	}
   187  
   188  	// set an cache entry
   189  	_, err = cache.Set(ctx, registry, scheme, key, func(c context.Context) (string, error) {
   190  		return "foobar", nil
   191  	})
   192  	if err != nil {
   193  		t.Fatalf("failed to set cache: %v", err)
   194  	}
   195  
   196  	// verify cache
   197  	got, err = cache.GetToken(ctx, registry, scheme, key)
   198  	if err != nil {
   199  		t.Fatalf("concurrentCache.GetToken() error = %v", err)
   200  	}
   201  	if want := "foobar"; got != want {
   202  		t.Errorf("concurrentCache.GetToken() = %v, want %v", got, want)
   203  	}
   204  
   205  	// verify cache of the previous registry as registries should not
   206  	// interference each other
   207  	registry = "localhost:5000"
   208  	got, err = cache.GetToken(ctx, registry, scheme, key)
   209  	if err != nil {
   210  		t.Fatalf("concurrentCache.GetToken() error = %v", err)
   211  	}
   212  	if want := "bar"; got != want {
   213  		t.Errorf("concurrentCache.GetToken() = %v, want %v", got, want)
   214  	}
   215  
   216  	// test other scheme
   217  	scheme = SchemeBasic
   218  	got, err = cache.GetToken(ctx, registry, scheme, key)
   219  	if want := errdef.ErrNotFound; err != want {
   220  		t.Fatalf("concurrentCache.GetToken() error = %v, wantErr %v", err, want)
   221  	}
   222  	if got != "" {
   223  		t.Errorf("concurrentCache.GetToken() = %v, want %v", got, "")
   224  	}
   225  
   226  	// set an cache entry
   227  	_, err = cache.Set(ctx, registry, scheme, key, func(c context.Context) (string, error) {
   228  		return "new scheme", nil
   229  	})
   230  	if err != nil {
   231  		t.Fatalf("failed to set cache: %v", err)
   232  	}
   233  
   234  	// verify cache
   235  	got, err = cache.GetToken(ctx, registry, scheme, key)
   236  	if err != nil {
   237  		t.Fatalf("concurrentCache.GetToken() error = %v", err)
   238  	}
   239  	if want := "new scheme"; got != want {
   240  		t.Errorf("concurrentCache.GetToken() = %v, want %v", got, want)
   241  	}
   242  
   243  	// cache of the previous scheme should be invalidated due to scheme change.
   244  	got, err = cache.GetToken(ctx, registry, SchemeBearer, key)
   245  	if want := errdef.ErrNotFound; err != want {
   246  		t.Fatalf("concurrentCache.GetToken() error = %v, wantErr %v", err, want)
   247  	}
   248  	if got != "" {
   249  		t.Errorf("concurrentCache.GetToken() = %v, want %v", got, "")
   250  	}
   251  }
   252  
   253  func Test_concurrentCache_Set(t *testing.T) {
   254  	registries := []string{
   255  		"localhost:5000",
   256  		"localhost:5001",
   257  	}
   258  	scheme := SchemeBearer
   259  	keys := []string{
   260  		"foo",
   261  		"bar",
   262  	}
   263  	count := len(registries) * len(keys)
   264  
   265  	ctx := context.Background()
   266  	cache := NewCache()
   267  
   268  	// first round of fetch
   269  	fetch := func(i int) func(context.Context) (string, error) {
   270  		return func(context.Context) (string, error) {
   271  			return strconv.Itoa(i), nil
   272  		}
   273  	}
   274  	var wg sync.WaitGroup
   275  	for i := 0; i < 10; i++ {
   276  		for j := 0; j < count; j++ {
   277  			wg.Add(1)
   278  			go func(i int) {
   279  				defer wg.Done()
   280  				registry := registries[i&1]
   281  				key := keys[(i>>1)&1]
   282  				got, err := cache.Set(ctx, registry, scheme, key, fetch(i))
   283  				if err != nil {
   284  					t.Errorf("concurrentCache.Set() error = %v", err)
   285  				}
   286  				if want := strconv.Itoa(i); got != want {
   287  					t.Errorf("concurrentCache.Set() = %v, want %v", got, want)
   288  				}
   289  			}(j)
   290  		}
   291  	}
   292  	wg.Wait()
   293  
   294  	for i := 0; i < count; i++ {
   295  		registry := registries[i&1]
   296  		key := keys[(i>>1)&1]
   297  
   298  		gotScheme, err := cache.GetScheme(ctx, registry)
   299  		if err != nil {
   300  			t.Fatalf("concurrentCache.GetScheme() error = %v", err)
   301  		}
   302  		if want := scheme; gotScheme != want {
   303  			t.Errorf("concurrentCache.GetScheme() = %v, want %v", gotScheme, want)
   304  		}
   305  
   306  		gotToken, err := cache.GetToken(ctx, registry, scheme, key)
   307  		if err != nil {
   308  			t.Fatalf("concurrentCache.GetToken() error = %v", err)
   309  		}
   310  		if want := strconv.Itoa(i); gotToken != want {
   311  			t.Errorf("concurrentCache.GetToken() = %v, want %v", gotToken, want)
   312  		}
   313  	}
   314  
   315  	// repeated fetch
   316  	fetch = func(i int) func(context.Context) (string, error) {
   317  		return func(context.Context) (string, error) {
   318  			return strconv.Itoa(i) + " repeated", nil
   319  		}
   320  	}
   321  	for i := 0; i < 10; i++ {
   322  		for j := 0; j < count; j++ {
   323  			wg.Add(1)
   324  			go func(i int) {
   325  				defer wg.Done()
   326  				registry := registries[i&1]
   327  				key := keys[(i>>1)&1]
   328  				got, err := cache.Set(ctx, registry, scheme, key, fetch(i))
   329  				if err != nil {
   330  					t.Errorf("concurrentCache.Set() error = %v", err)
   331  				}
   332  				if want := strconv.Itoa(i) + " repeated"; got != want {
   333  					t.Errorf("concurrentCache.Set() = %v, want %v", got, want)
   334  				}
   335  			}(j)
   336  		}
   337  	}
   338  	wg.Wait()
   339  
   340  	for i := 0; i < count; i++ {
   341  		registry := registries[i&1]
   342  		key := keys[(i>>1)&1]
   343  
   344  		gotScheme, err := cache.GetScheme(ctx, registry)
   345  		if err != nil {
   346  			t.Fatalf("concurrentCache.GetScheme() error = %v", err)
   347  		}
   348  		if want := scheme; gotScheme != want {
   349  			t.Errorf("concurrentCache.GetScheme() = %v, want %v", gotScheme, want)
   350  		}
   351  
   352  		gotToken, err := cache.GetToken(ctx, registry, scheme, key)
   353  		if err != nil {
   354  			t.Fatalf("concurrentCache.GetToken() error = %v", err)
   355  		}
   356  		if want := strconv.Itoa(i) + " repeated"; gotToken != want {
   357  			t.Errorf("concurrentCache.GetToken() = %v, want %v", gotToken, want)
   358  		}
   359  	}
   360  }
   361  
   362  func Test_concurrentCache_Set_Fetch_Once(t *testing.T) {
   363  	registries := []string{
   364  		"localhost:5000",
   365  		"localhost:5001",
   366  	}
   367  	schemes := []Scheme{
   368  		SchemeBasic,
   369  		SchemeBearer,
   370  	}
   371  	keys := []string{
   372  		"foo",
   373  		"bar",
   374  	}
   375  	count := make([]int64, len(registries)*len(schemes)*len(keys))
   376  	fetch := func(i int) func(context.Context) (string, error) {
   377  		return func(context.Context) (string, error) {
   378  			time.Sleep(500 * time.Millisecond)
   379  			atomic.AddInt64(&count[i], 1)
   380  			return strconv.Itoa(i), nil
   381  		}
   382  	}
   383  
   384  	ctx := context.Background()
   385  	cache := NewCache()
   386  
   387  	// first round of fetch
   388  	var wg sync.WaitGroup
   389  	for i := 0; i < 10; i++ {
   390  		for j := 0; j < len(count); j++ {
   391  			wg.Add(1)
   392  			go func(i int) {
   393  				defer wg.Done()
   394  				registry := registries[i&1]
   395  				scheme := schemes[(i>>1)&1]
   396  				key := keys[(i>>2)&1]
   397  				got, err := cache.Set(ctx, registry, scheme, key, fetch(i))
   398  				if err != nil {
   399  					t.Errorf("concurrentCache.Set() error = %v", err)
   400  				}
   401  				if want := strconv.Itoa(i); got != want {
   402  					t.Errorf("concurrentCache.Set() = %v, want %v", got, want)
   403  				}
   404  			}(j)
   405  		}
   406  	}
   407  	wg.Wait()
   408  
   409  	for i := 0; i < len(count); i++ {
   410  		if got := count[i]; got != 1 {
   411  			t.Errorf("fetch is called more than once: %d", got)
   412  		}
   413  	}
   414  
   415  	// repeated fetch
   416  	for i := 0; i < 10; i++ {
   417  		for j := 0; j < len(count); j++ {
   418  			wg.Add(1)
   419  			go func(i int) {
   420  				defer wg.Done()
   421  				registry := registries[i&1]
   422  				scheme := schemes[(i>>1)&1]
   423  				key := keys[(i>>2)&1]
   424  				got, err := cache.Set(ctx, registry, scheme, key, fetch(i))
   425  				if err != nil {
   426  					t.Errorf("concurrentCache.Set() error = %v", err)
   427  				}
   428  				if want := strconv.Itoa(i); got != want {
   429  					t.Errorf("concurrentCache.Set() = %v, want %v", got, want)
   430  				}
   431  			}(j)
   432  		}
   433  	}
   434  	wg.Wait()
   435  
   436  	for i := 0; i < len(count); i++ {
   437  		if got := count[i]; got != 2 {
   438  			t.Errorf("fetch is called more than once: %d", got)
   439  		}
   440  	}
   441  }
   442  
   443  func Test_concurrentCache_Set_Fetch_Failure(t *testing.T) {
   444  	registries := []string{
   445  		"localhost:5000",
   446  		"localhost:5001",
   447  	}
   448  	scheme := SchemeBearer
   449  	keys := []string{
   450  		"foo",
   451  		"bar",
   452  	}
   453  	count := len(registries) * len(keys)
   454  
   455  	ctx := context.Background()
   456  	cache := NewCache()
   457  
   458  	// first round of fetch
   459  	fetch := func(i int) func(context.Context) (string, error) {
   460  		return func(context.Context) (string, error) {
   461  			return "", errors.New(strconv.Itoa(i))
   462  		}
   463  	}
   464  	var wg sync.WaitGroup
   465  	for i := 0; i < 10; i++ {
   466  		for j := 0; j < count; j++ {
   467  			wg.Add(1)
   468  			go func(i int) {
   469  				defer wg.Done()
   470  				registry := registries[i&1]
   471  				key := keys[(i>>1)&1]
   472  				_, err := cache.Set(ctx, registry, scheme, key, fetch(i))
   473  				if want := strconv.Itoa(i); err == nil || err.Error() != want {
   474  					t.Errorf("concurrentCache.Set() error = %v, wantErr %v", err, want)
   475  				}
   476  			}(j)
   477  		}
   478  	}
   479  	wg.Wait()
   480  
   481  	for i := 0; i < count; i++ {
   482  		registry := registries[i&1]
   483  		key := keys[(i>>1)&1]
   484  
   485  		_, err := cache.GetScheme(ctx, registry)
   486  		if want := errdef.ErrNotFound; err != want {
   487  			t.Fatalf("concurrentCache.GetScheme() error = %v, wantErr %v", err, want)
   488  		}
   489  
   490  		_, err = cache.GetToken(ctx, registry, scheme, key)
   491  		if want := errdef.ErrNotFound; err != want {
   492  			t.Errorf("concurrentCache.GetToken() error = %v, wantErr %v", err, want)
   493  		}
   494  	}
   495  
   496  	// repeated fetch
   497  	fetch = func(i int) func(context.Context) (string, error) {
   498  		return func(context.Context) (string, error) {
   499  			return strconv.Itoa(i), nil
   500  		}
   501  	}
   502  	for i := 0; i < 10; i++ {
   503  		for j := 0; j < count; j++ {
   504  			wg.Add(1)
   505  			go func(i int) {
   506  				defer wg.Done()
   507  				registry := registries[i&1]
   508  				key := keys[(i>>1)&1]
   509  				got, err := cache.Set(ctx, registry, scheme, key, fetch(i))
   510  				if err != nil {
   511  					t.Errorf("concurrentCache.Set() error = %v", err)
   512  				}
   513  				if want := strconv.Itoa(i); got != want {
   514  					t.Errorf("concurrentCache.Set() = %v, want %v", got, want)
   515  				}
   516  			}(j)
   517  		}
   518  	}
   519  	wg.Wait()
   520  
   521  	for i := 0; i < count; i++ {
   522  		registry := registries[i&1]
   523  		key := keys[(i>>1)&1]
   524  
   525  		gotScheme, err := cache.GetScheme(ctx, registry)
   526  		if err != nil {
   527  			t.Fatalf("concurrentCache.GetScheme() error = %v", err)
   528  		}
   529  		if want := scheme; gotScheme != want {
   530  			t.Errorf("concurrentCache.GetScheme() = %v, want %v", gotScheme, want)
   531  		}
   532  
   533  		gotToken, err := cache.GetToken(ctx, registry, scheme, key)
   534  		if err != nil {
   535  			t.Fatalf("concurrentCache.GetToken() error = %v", err)
   536  		}
   537  		if want := strconv.Itoa(i); gotToken != want {
   538  			t.Errorf("concurrentCache.GetToken() = %v, want %v", gotToken, want)
   539  		}
   540  	}
   541  }
   542  

View as plain text