istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/config/monitor/monitor_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 monitor 16 17 import ( 18 "errors" 19 "testing" 20 "time" 21 22 . "github.com/onsi/gomega" 23 24 networking "istio.io/api/networking/v1alpha3" 25 "istio.io/istio/pilot/pkg/config/memory" 26 "istio.io/istio/pkg/config" 27 "istio.io/istio/pkg/config/schema/collection" 28 "istio.io/istio/pkg/config/schema/collections" 29 "istio.io/istio/pkg/config/schema/gvk" 30 "istio.io/istio/pkg/test/util/retry" 31 ) 32 33 var createConfigSet = []*config.Config{ 34 { 35 Meta: config.Meta{ 36 Name: "magic", 37 GroupVersionKind: gvk.Gateway, 38 }, 39 Spec: &networking.Gateway{ 40 Servers: []*networking.Server{ 41 { 42 Port: &networking.Port{ 43 Number: 80, 44 Protocol: "HTTP", 45 Name: "http", 46 }, 47 Hosts: []string{"*.example.com"}, 48 }, 49 }, 50 }, 51 }, 52 } 53 54 var updateConfigSet = []*config.Config{ 55 { 56 Meta: config.Meta{ 57 Name: "magic", 58 GroupVersionKind: gvk.Gateway, 59 }, 60 Spec: &networking.Gateway{ 61 Servers: []*networking.Server{ 62 { 63 Port: &networking.Port{ 64 Number: 80, 65 Protocol: "HTTP2", 66 Name: "http", 67 }, 68 Hosts: []string{"*.example.com"}, 69 }, 70 }, 71 }, 72 }, 73 } 74 75 func TestMonitorForChange(t *testing.T) { 76 g := NewWithT(t) 77 78 store := memory.Make(collection.SchemasFor(collections.Gateway)) 79 80 var ( 81 callCount int 82 configs []*config.Config 83 err error 84 ) 85 86 someConfigFunc := func() ([]*config.Config, error) { 87 switch callCount { 88 case 0: 89 configs = createConfigSet 90 err = nil 91 case 3: 92 configs = updateConfigSet 93 case 6: 94 configs = []*config.Config{} 95 } 96 97 callCount++ 98 return configs, err 99 } 100 mon := NewMonitor("", store, someConfigFunc, "") 101 stop := make(chan struct{}) 102 defer func() { close(stop) }() 103 mon.Start(stop) 104 105 go func() { 106 for i := 0; i < 10; i++ { 107 select { 108 case <-stop: 109 return 110 case mon.updateCh <- struct{}{}: 111 } 112 time.Sleep(time.Millisecond * 100) 113 } 114 }() 115 g.Eventually(func() error { 116 c := store.List(gvk.Gateway, "") 117 118 if len(c) != 1 { 119 return errors.New("no configs") 120 } 121 122 if c[0].Meta.Name != "magic" { 123 return errors.New("wrong config") 124 } 125 126 return nil 127 }).Should(Succeed()) 128 129 g.Eventually(func() error { 130 c := store.List(gvk.Gateway, "") 131 if len(c) == 0 { 132 return errors.New("no config") 133 } 134 135 gateway := c[0].Spec.(*networking.Gateway) 136 if gateway.Servers[0].Port.Protocol != "HTTP2" { 137 return errors.New("protocol has not been updated") 138 } 139 140 return nil 141 }).Should(Succeed()) 142 143 g.Eventually(func() []config.Config { 144 return store.List(gvk.Gateway, "") 145 }).Should(HaveLen(0)) 146 } 147 148 func TestMonitorFileSnapshot(t *testing.T) { 149 ts := &testState{ 150 ConfigFiles: map[string][]byte{"gateway.yml": []byte(statusRegressionYAML)}, 151 } 152 153 ts.testSetup(t) 154 155 store := memory.Make(collection.SchemasFor(collections.Gateway)) 156 fileWatcher := NewFileSnapshot(ts.rootPath, collection.SchemasFor(), "foo") 157 158 mon := NewMonitor("", store, fileWatcher.ReadConfigFiles, "") 159 stop := make(chan struct{}) 160 defer func() { close(stop) }() 161 mon.Start(stop) 162 retry.UntilOrFail(t, func() bool { return store.Get(gvk.Gateway, "test", "test-1") != nil }) 163 } 164 165 func TestMonitorForError(t *testing.T) { 166 g := NewWithT(t) 167 168 store := memory.Make(collection.SchemasFor(collections.Gateway)) 169 170 var ( 171 callCount int 172 configs []*config.Config 173 err error 174 ) 175 176 delay := make(chan struct{}, 1) 177 178 someConfigFunc := func() ([]*config.Config, error) { 179 switch callCount { 180 case 0: 181 configs = createConfigSet 182 err = nil 183 case 3: 184 configs = nil 185 err = errors.New("snapshotFunc can't connect") 186 delay <- struct{}{} 187 } 188 189 callCount++ 190 return configs, err 191 } 192 mon := NewMonitor("", store, someConfigFunc, "") 193 stop := make(chan struct{}) 194 defer func() { close(stop) }() 195 mon.Start(stop) 196 197 go func() { 198 updateTicker := time.NewTicker(100 * time.Millisecond) 199 numUpdates := 10 200 for { 201 select { 202 case <-stop: 203 updateTicker.Stop() 204 return 205 case <-updateTicker.C: 206 mon.updateCh <- struct{}{} 207 numUpdates-- 208 if numUpdates == 0 { 209 updateTicker.Stop() 210 return 211 } 212 } 213 } 214 }() 215 // Test ensures that after a coplilot connection error the data remains 216 // nil data return and error return keeps the existing data aka createConfigSet 217 <-delay 218 g.Eventually(func() error { 219 c := store.List(gvk.Gateway, "") 220 221 if len(c) != 1 { 222 return errors.New("config files erased on Copilot error") 223 } 224 225 return nil 226 }).Should(Succeed()) 227 }