istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/leaderelection/k8sleaderelection/healthzadaptor_test.go (about) 1 /* 2 Copyright 2015 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // nolint 18 package k8sleaderelection 19 20 import ( 21 "context" 22 "fmt" 23 "net/http" 24 "testing" 25 "time" 26 27 clock "k8s.io/utils/clock/testing" 28 29 rl "istio.io/istio/pilot/pkg/leaderelection/k8sleaderelection/k8sresourcelock" 30 ) 31 32 type fakeLock struct { 33 identity string 34 key string 35 } 36 37 // Get is a dummy to allow us to have a fakeLock for testing. 38 func (fl *fakeLock) Get(ctx context.Context) (ler *rl.LeaderElectionRecord, rawRecord []byte, err error) { 39 return nil, nil, nil 40 } 41 42 // Create is a dummy to allow us to have a fakeLock for testing. 43 func (fl *fakeLock) Create(ctx context.Context, ler rl.LeaderElectionRecord) error { 44 return nil 45 } 46 47 // Update is a dummy to allow us to have a fakeLock for testing. 48 func (fl *fakeLock) Update(ctx context.Context, ler rl.LeaderElectionRecord) error { 49 return nil 50 } 51 52 // RecordEvent is a dummy to allow us to have a fakeLock for testing. 53 func (fl *fakeLock) RecordEvent(string) {} 54 55 // Identity is a dummy to allow us to have a fakeLock for testing. 56 func (fl *fakeLock) Identity() string { 57 return fl.identity 58 } 59 60 // Key is a dummy to allow us to have a fakeLock for testing. 61 func (fl *fakeLock) Key() string { 62 return fl.key 63 } 64 65 // Describe is a dummy to allow us to have a fakeLock for testing. 66 func (fl *fakeLock) Describe() string { 67 return "Dummy implementation of lock for testing" 68 } 69 70 // TestLeaderElectionHealthChecker tests that the healthcheck for leader election handles its edge cases. 71 func TestLeaderElectionHealthChecker(t *testing.T) { 72 current := time.Now() 73 req := &http.Request{} 74 75 tests := []struct { 76 description string 77 expected error 78 adaptorTimeout time.Duration 79 elector *LeaderElector 80 }{ 81 { 82 description: "call check before leader elector initialized", 83 expected: nil, 84 adaptorTimeout: time.Second * 20, 85 elector: nil, 86 }, 87 { 88 description: "call check when the lease is far expired", 89 expected: fmt.Errorf("failed election to renew leadership on lease %s", "foo"), 90 adaptorTimeout: time.Second * 20, 91 elector: &LeaderElector{ 92 config: LeaderElectionConfig{ 93 Lock: &fakeLock{identity: "healthTest"}, 94 LeaseDuration: time.Minute, 95 Name: "foo", 96 }, 97 observedRecord: rl.LeaderElectionRecord{ 98 HolderIdentity: "healthTest", 99 }, 100 observedTime: current, 101 clock: clock.NewFakeClock(current.Add(time.Hour)), 102 }, 103 }, 104 { 105 description: "call check when the lease is far expired but held by another server", 106 expected: nil, 107 adaptorTimeout: time.Second * 20, 108 elector: &LeaderElector{ 109 config: LeaderElectionConfig{ 110 Lock: &fakeLock{identity: "healthTest"}, 111 LeaseDuration: time.Minute, 112 Name: "foo", 113 }, 114 observedRecord: rl.LeaderElectionRecord{ 115 HolderIdentity: "otherServer", 116 }, 117 observedTime: current, 118 clock: clock.NewFakeClock(current.Add(time.Hour)), 119 }, 120 }, 121 { 122 description: "call check when the lease is not expired", 123 expected: nil, 124 adaptorTimeout: time.Second * 20, 125 elector: &LeaderElector{ 126 config: LeaderElectionConfig{ 127 Lock: &fakeLock{identity: "healthTest"}, 128 LeaseDuration: time.Minute, 129 Name: "foo", 130 }, 131 observedRecord: rl.LeaderElectionRecord{ 132 HolderIdentity: "healthTest", 133 }, 134 observedTime: current, 135 clock: clock.NewFakeClock(current), 136 }, 137 }, 138 { 139 description: "call check when the lease is expired but inside the timeout", 140 expected: nil, 141 adaptorTimeout: time.Second * 20, 142 elector: &LeaderElector{ 143 config: LeaderElectionConfig{ 144 Lock: &fakeLock{identity: "healthTest"}, 145 LeaseDuration: time.Minute, 146 Name: "foo", 147 }, 148 observedRecord: rl.LeaderElectionRecord{ 149 HolderIdentity: "healthTest", 150 }, 151 observedTime: current, 152 clock: clock.NewFakeClock(current.Add(time.Minute).Add(time.Second)), 153 }, 154 }, 155 } 156 157 for _, test := range tests { 158 adaptor := NewLeaderHealthzAdaptor(test.adaptorTimeout) 159 if adaptor.le != nil { 160 t.Errorf("[%s] leaderChecker started with a LeaderElector %v", test.description, adaptor.le) 161 } 162 if test.elector != nil { 163 test.elector.config.WatchDog = adaptor 164 adaptor.SetLeaderElection(test.elector) 165 if adaptor.le == nil { 166 t.Errorf("[%s] adaptor failed to set the LeaderElector", test.description) 167 } 168 } 169 err := adaptor.Check(req) 170 if test.expected == nil { 171 if err == nil { 172 continue 173 } 174 t.Errorf("[%s] called check, expected no error but received \"%v\"", test.description, err) 175 } else { 176 if err == nil { 177 t.Errorf("[%s] called check and failed to received the expected error \"%v\"", test.description, test.expected) 178 } 179 if err.Error() != test.expected.Error() { 180 t.Errorf("[%s] called check, expected %v, received %v", test.description, test.expected, err) 181 } 182 } 183 } 184 }