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 }