istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/server/instance_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 server_test 16 17 import ( 18 "errors" 19 "testing" 20 "time" 21 22 . "github.com/onsi/gomega" 23 "go.uber.org/atomic" 24 25 "istio.io/istio/pilot/pkg/server" 26 "istio.io/istio/pkg/test" 27 ) 28 29 func TestStartWithError(t *testing.T) { 30 g := NewWithT(t) 31 32 inst := server.New() 33 expected := errors.New("fake") 34 inst.RunComponent("fake", func(stop <-chan struct{}) error { 35 return expected 36 }) 37 38 stop := newReclosableChannel() 39 t.Cleanup(stop.Close) 40 g.Expect(inst.Start(stop.c)).To(Equal(expected)) 41 } 42 43 func TestStartWithNoError(t *testing.T) { 44 g := NewWithT(t) 45 46 inst := server.New() 47 c := newFakeComponent(0, test.NewStop(t)) 48 inst.RunComponent("fake", c.Run) 49 50 stop := newReclosableChannel() 51 t.Cleanup(stop.Close) 52 g.Expect(inst.Start(stop.c)).To(BeNil()) 53 g.Expect(c.started.Load()).To(BeTrue()) 54 } 55 56 func TestRunComponentsAfterStart(t *testing.T) { 57 longDuration := 10 * time.Second 58 shortDuration := 10 * time.Millisecond 59 // Just used to make sure we do not leak goroutines 60 stop := test.NewStop(t) 61 cases := []struct { 62 name string 63 c *fakeComponent 64 async bool 65 wait bool 66 }{ 67 { 68 name: "RunComponent", 69 // Use a large duration - it will not complete before the end of the test. 70 // This is used to verify that we don't wait for it while shutting down. 71 c: newFakeComponent(longDuration, stop), 72 async: false, 73 wait: false, 74 }, 75 { 76 name: "RunComponentAsync", 77 // Use a large duration - it will not complete before the end of the test. 78 // This is used to verify that we don't wait for it while shutting down. 79 c: newFakeComponent(longDuration, stop), 80 async: true, 81 wait: false, 82 }, 83 { 84 name: "RunComponentAsyncAndWait", 85 c: newFakeComponent(shortDuration, stop), 86 async: true, 87 wait: true, 88 }, 89 } 90 for _, c := range cases { 91 c := c 92 t.Run(c.name, func(t *testing.T) { 93 g := NewWithT(t) 94 95 stop := newReclosableChannel() 96 t.Cleanup(stop.Close) 97 inst := server.New() 98 g.Expect(inst.Start(stop.c)).To(BeNil()) 99 100 component := c.c.Run 101 go func() { 102 if c.async { 103 if c.wait { 104 inst.RunComponentAsyncAndWait("test", component) 105 } else { 106 inst.RunComponentAsync("test", component) 107 } 108 } else { 109 inst.RunComponent("test", component) 110 } 111 }() 112 113 // Ensure that the component is started. 114 g.Eventually(func() bool { 115 return c.c.started.Load() 116 }).Should(BeTrue()) 117 118 // Stop before the tasks end. 119 stop.Close() 120 if c.wait { 121 // Add a little buffer to the task duration. 122 totalWaitTime := shortDuration + (1 * time.Second) 123 g.Eventually(func() bool { 124 return c.c.completed.Load() 125 }, totalWaitTime).Should(BeTrue()) 126 } else { 127 g.Expect(c.c.completed.Load()).Should(BeFalse()) 128 } 129 }) 130 } 131 } 132 133 type reclosableChannel struct { 134 c chan struct{} 135 closed bool 136 } 137 138 func newReclosableChannel() *reclosableChannel { 139 return &reclosableChannel{ 140 c: make(chan struct{}), 141 } 142 } 143 144 func (c *reclosableChannel) Close() { 145 if !c.closed { 146 c.closed = true 147 close(c.c) 148 } 149 } 150 151 type fakeComponent struct { 152 started *atomic.Bool 153 completed *atomic.Bool 154 d time.Duration 155 stop chan struct{} 156 } 157 158 func newFakeComponent(d time.Duration, stop chan struct{}) *fakeComponent { 159 return &fakeComponent{ 160 started: atomic.NewBool(false), 161 completed: atomic.NewBool(false), 162 d: d, 163 stop: stop, 164 } 165 } 166 167 func (c *fakeComponent) Run(stop <-chan struct{}) error { 168 c.started.Store(true) 169 select { 170 case <-time.After(c.d): 171 case <-c.stop: // ignore incoming stop; for test purposes we use our own stop 172 } 173 c.completed.Store(true) 174 return nil 175 }