istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/config/aggregate/config_test.go (about)

     1  // Copyright Istio Authors
     2  //
     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 aggregate
    16  
    17  import (
    18  	"strings"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/google/go-cmp/cmp"
    23  	. "github.com/onsi/gomega"
    24  	"go.uber.org/atomic"
    25  
    26  	"istio.io/istio/pilot/pkg/config/memory"
    27  	"istio.io/istio/pilot/pkg/model"
    28  	"istio.io/istio/pkg/config"
    29  	"istio.io/istio/pkg/config/schema/collection"
    30  	"istio.io/istio/pkg/config/schema/collections"
    31  	"istio.io/istio/pkg/config/schema/gvk"
    32  	"istio.io/istio/pkg/config/schema/resource"
    33  	"istio.io/istio/pkg/test/util/retry"
    34  )
    35  
    36  func TestAggregateStoreBasicMake(t *testing.T) {
    37  	g := NewWithT(t)
    38  
    39  	schema1 := collections.HTTPRoute
    40  	schema2 := collections.GatewayClass
    41  	store1 := memory.Make(collection.SchemasFor(schema1))
    42  	store2 := memory.Make(collection.SchemasFor(schema2))
    43  
    44  	stores := []model.ConfigStore{store1, store2}
    45  
    46  	store, err := makeStore(stores, nil)
    47  	g.Expect(err).NotTo(HaveOccurred())
    48  
    49  	schemas := store.Schemas()
    50  	g.Expect(cmp.Diff(schemas, collection.SchemasFor(schema1, schema2))).To(BeEmpty())
    51  }
    52  
    53  func TestAggregateStoreMakeValidationFailure(t *testing.T) {
    54  	g := NewWithT(t)
    55  
    56  	store1 := memory.Make(collection.SchemasFor(schemaFor("SomeConfig", "broken message name")))
    57  
    58  	stores := []model.ConfigStore{store1}
    59  
    60  	store, err := makeStore(stores, nil)
    61  	g.Expect(err).To(MatchError(ContainSubstring("not found: broken message name")))
    62  	g.Expect(store).To(BeNil())
    63  }
    64  
    65  func TestAggregateStoreGet(t *testing.T) {
    66  	g := NewWithT(t)
    67  
    68  	store1 := memory.Make(collection.SchemasFor(collections.GatewayClass))
    69  	store2 := memory.Make(collection.SchemasFor(collections.GatewayClass))
    70  
    71  	configReturn := &config.Config{
    72  		Meta: config.Meta{
    73  			GroupVersionKind: gvk.GatewayClass,
    74  			Name:             "other",
    75  		},
    76  	}
    77  
    78  	_, err := store1.Create(*configReturn)
    79  	g.Expect(err).NotTo(HaveOccurred())
    80  
    81  	stores := []model.ConfigStore{store1, store2}
    82  
    83  	store, err := makeStore(stores, nil)
    84  	g.Expect(err).NotTo(HaveOccurred())
    85  
    86  	c := store.Get(gvk.GatewayClass, "other", "")
    87  	g.Expect(c.Name).To(Equal(configReturn.Name))
    88  }
    89  
    90  func TestAggregateStoreList(t *testing.T) {
    91  	g := NewWithT(t)
    92  
    93  	store1 := memory.Make(collection.SchemasFor(collections.HTTPRoute))
    94  	store2 := memory.Make(collection.SchemasFor(collections.HTTPRoute))
    95  
    96  	if _, err := store1.Create(config.Config{
    97  		Meta: config.Meta{
    98  			GroupVersionKind: gvk.HTTPRoute,
    99  			Name:             "other",
   100  		},
   101  	}); err != nil {
   102  		t.Fatal(err)
   103  	}
   104  	if _, err := store2.Create(config.Config{
   105  		Meta: config.Meta{
   106  			GroupVersionKind: gvk.HTTPRoute,
   107  			Name:             "another",
   108  		},
   109  	}); err != nil {
   110  		t.Fatal(err)
   111  	}
   112  
   113  	stores := []model.ConfigStore{store1, store2}
   114  
   115  	store, err := makeStore(stores, nil)
   116  	g.Expect(err).NotTo(HaveOccurred())
   117  
   118  	l := store.List(gvk.HTTPRoute, "")
   119  	g.Expect(l).To(HaveLen(2))
   120  }
   121  
   122  func TestAggregateStoreWrite(t *testing.T) {
   123  	g := NewWithT(t)
   124  
   125  	store1 := memory.Make(collection.SchemasFor(collections.HTTPRoute))
   126  	store2 := memory.Make(collection.SchemasFor(collections.HTTPRoute))
   127  
   128  	stores := []model.ConfigStore{store1, store2}
   129  
   130  	store, err := makeStore(stores, store1)
   131  	g.Expect(err).NotTo(HaveOccurred())
   132  
   133  	if _, err := store.Create(config.Config{
   134  		Meta: config.Meta{
   135  			GroupVersionKind: gvk.HTTPRoute,
   136  			Name:             "other",
   137  		},
   138  	}); err != nil {
   139  		t.Fatal(err)
   140  	}
   141  
   142  	la := store.List(gvk.HTTPRoute, "")
   143  	g.Expect(la).To(HaveLen(1))
   144  	g.Expect(la[0].Name).To(Equal("other"))
   145  
   146  	l := store1.List(gvk.HTTPRoute, "")
   147  	g.Expect(l).To(HaveLen(1))
   148  	g.Expect(l[0].Name).To(Equal("other"))
   149  
   150  	// Check the aggregated and individual store return identical response
   151  	g.Expect(la).To(BeEquivalentTo(l))
   152  
   153  	l = store2.List(gvk.HTTPRoute, "")
   154  	g.Expect(l).To(HaveLen(0))
   155  }
   156  
   157  func TestAggregateStoreWriteWithoutWriter(t *testing.T) {
   158  	g := NewWithT(t)
   159  
   160  	store1 := memory.Make(collection.SchemasFor(collections.HTTPRoute))
   161  	store2 := memory.Make(collection.SchemasFor(collections.HTTPRoute))
   162  
   163  	stores := []model.ConfigStore{store1, store2}
   164  
   165  	store, err := makeStore(stores, nil)
   166  	g.Expect(err).NotTo(HaveOccurred())
   167  
   168  	if _, err := store.Create(config.Config{
   169  		Meta: config.Meta{
   170  			GroupVersionKind: gvk.HTTPRoute,
   171  			Name:             "other",
   172  		},
   173  	}); err != errorUnsupported {
   174  		t.Fatalf("unexpected error, want %v got %v", errorUnsupported, err)
   175  	}
   176  }
   177  
   178  func TestAggregateStoreFails(t *testing.T) {
   179  	g := NewWithT(t)
   180  
   181  	store1 := memory.Make(collection.SchemasFor(schemaFor("OtherConfig", "istio.networking.v1alpha3.Gateway")))
   182  
   183  	stores := []model.ConfigStore{store1}
   184  
   185  	store, err := makeStore(stores, nil)
   186  	g.Expect(err).NotTo(HaveOccurred())
   187  
   188  	t.Run("Fails to Delete", func(t *testing.T) {
   189  		g := NewWithT(t)
   190  
   191  		err = store.Delete(config.GroupVersionKind{Kind: "not"}, "gonna", "work", nil)
   192  		g.Expect(err).To(MatchError(ContainSubstring("unsupported operation")))
   193  	})
   194  
   195  	t.Run("Fails to Create", func(t *testing.T) {
   196  		g := NewWithT(t)
   197  
   198  		c, err := store.Create(config.Config{})
   199  		g.Expect(err).To(MatchError(ContainSubstring("unsupported operation")))
   200  		g.Expect(c).To(BeEmpty())
   201  	})
   202  
   203  	t.Run("Fails to Update", func(t *testing.T) {
   204  		g := NewWithT(t)
   205  
   206  		c, err := store.Update(config.Config{})
   207  		g.Expect(err).To(MatchError(ContainSubstring("unsupported operation")))
   208  		g.Expect(c).To(BeEmpty())
   209  	})
   210  }
   211  
   212  func TestAggregateStoreCache(t *testing.T) {
   213  	stop := make(chan struct{})
   214  	defer func() { close(stop) }()
   215  
   216  	store1 := memory.Make(collection.SchemasFor(collections.HTTPRoute))
   217  	controller1 := memory.NewController(store1)
   218  	go controller1.Run(stop)
   219  
   220  	store2 := memory.Make(collection.SchemasFor(collections.HTTPRoute))
   221  	controller2 := memory.NewController(store2)
   222  	go controller2.Run(stop)
   223  
   224  	stores := []model.ConfigStoreController{controller1, controller2}
   225  
   226  	cacheStore, err := MakeCache(stores)
   227  	if err != nil {
   228  		t.Fatal(err)
   229  	}
   230  
   231  	t.Run("it registers an event handler", func(t *testing.T) {
   232  		handled := atomic.NewBool(false)
   233  		cacheStore.RegisterEventHandler(gvk.HTTPRoute, func(config.Config, config.Config, model.Event) {
   234  			handled.Store(true)
   235  		})
   236  
   237  		_, err := controller1.Create(config.Config{
   238  			Meta: config.Meta{
   239  				GroupVersionKind: gvk.HTTPRoute,
   240  				Name:             "another",
   241  			},
   242  		})
   243  		if err != nil {
   244  			t.Fatal(err)
   245  		}
   246  
   247  		retry.UntilOrFail(t, handled.Load, retry.Timeout(time.Second))
   248  	})
   249  }
   250  
   251  func schemaFor(kind, proto string) resource.Schema {
   252  	return resource.Builder{
   253  		Kind:   kind,
   254  		Plural: strings.ToLower(kind) + "s",
   255  		Proto:  proto,
   256  	}.BuildNoValidate()
   257  }