k8s.io/apiserver@v0.31.1/pkg/admission/configuration/configuration_manager.go (about) 1 /* 2 Copyright 2017 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 package configuration 18 19 import ( 20 "fmt" 21 "sync" 22 "time" 23 24 "k8s.io/apimachinery/pkg/runtime" 25 "k8s.io/apimachinery/pkg/util/wait" 26 ) 27 28 const ( 29 defaultInterval = 1 * time.Second 30 defaultFailureThreshold = 5 31 defaultBootstrapRetries = 5 32 defaultBootstrapGraceperiod = 5 * time.Second 33 ) 34 35 var ( 36 ErrNotReady = fmt.Errorf("configuration is not ready") 37 ErrDisabled = fmt.Errorf("disabled") 38 ) 39 40 type getFunc func() (runtime.Object, error) 41 42 // When running, poller calls `get` every `interval`. If `get` is 43 // successful, `Ready()` returns ready and `configuration()` returns the 44 // `mergedConfiguration`; if `get` has failed more than `failureThreshold ` times, 45 // `Ready()` returns not ready and `configuration()` returns nil configuration. 46 // In an HA setup, the poller is consistent only if the `get` is 47 // doing consistent read. 48 type poller struct { 49 // a function to consistently read the latest configuration 50 get getFunc 51 // consistent read interval 52 // read-only 53 interval time.Duration 54 // if the number of consecutive read failure equals or exceeds the failureThreshold , the 55 // configuration is regarded as not ready. 56 // read-only 57 failureThreshold int 58 // number of consecutive failures so far. 59 failures int 60 // If the poller has passed the bootstrap phase. The poller is considered 61 // bootstrapped either bootstrapGracePeriod after the first call of 62 // configuration(), or when setConfigurationAndReady() is called, whichever 63 // comes first. 64 bootstrapped bool 65 // configuration() retries bootstrapRetries times if poller is not bootstrapped 66 // read-only 67 bootstrapRetries int 68 // Grace period for bootstrapping 69 // read-only 70 bootstrapGracePeriod time.Duration 71 once sync.Once 72 // if the configuration is regarded as ready. 73 ready bool 74 mergedConfiguration runtime.Object 75 lastErr error 76 // lock must be hold when reading/writing the data fields of poller. 77 lock sync.RWMutex 78 } 79 80 func newPoller(get getFunc) *poller { 81 p := poller{ 82 get: get, 83 interval: defaultInterval, 84 failureThreshold: defaultFailureThreshold, 85 bootstrapRetries: defaultBootstrapRetries, 86 bootstrapGracePeriod: defaultBootstrapGraceperiod, 87 } 88 return &p 89 } 90 91 func (a *poller) lastError(err error) { 92 a.lock.Lock() 93 defer a.lock.Unlock() 94 a.lastErr = err 95 } 96 97 func (a *poller) notReady() { 98 a.lock.Lock() 99 defer a.lock.Unlock() 100 a.ready = false 101 } 102 103 func (a *poller) bootstrapping() { 104 // bootstrapGracePeriod is read-only, so no lock is required 105 timer := time.NewTimer(a.bootstrapGracePeriod) 106 go func() { 107 defer timer.Stop() 108 <-timer.C 109 a.lock.Lock() 110 defer a.lock.Unlock() 111 a.bootstrapped = true 112 }() 113 } 114 115 // If the poller is not bootstrapped yet, the configuration() gets a few chances 116 // to retry. This hides transient failures during system startup. 117 func (a *poller) configuration() (runtime.Object, error) { 118 a.once.Do(a.bootstrapping) 119 a.lock.RLock() 120 defer a.lock.RUnlock() 121 retries := 1 122 if !a.bootstrapped { 123 retries = a.bootstrapRetries 124 } 125 for count := 0; count < retries; count++ { 126 if count > 0 { 127 a.lock.RUnlock() 128 time.Sleep(a.interval) 129 a.lock.RLock() 130 } 131 if a.ready { 132 return a.mergedConfiguration, nil 133 } 134 } 135 if a.lastErr != nil { 136 return nil, a.lastErr 137 } 138 return nil, ErrNotReady 139 } 140 141 func (a *poller) setConfigurationAndReady(value runtime.Object) { 142 a.lock.Lock() 143 defer a.lock.Unlock() 144 a.bootstrapped = true 145 a.mergedConfiguration = value 146 a.ready = true 147 a.lastErr = nil 148 } 149 150 func (a *poller) Run(stopCh <-chan struct{}) { 151 go wait.Until(a.sync, a.interval, stopCh) 152 } 153 154 func (a *poller) sync() { 155 configuration, err := a.get() 156 if err != nil { 157 a.failures++ 158 a.lastError(err) 159 if a.failures >= a.failureThreshold { 160 a.notReady() 161 } 162 return 163 } 164 a.failures = 0 165 a.setConfigurationAndReady(configuration) 166 }