github.com/fafucoder/cilium@v1.6.11/pkg/controller/manager.go (about) 1 // Copyright 2018 Authors of Cilium 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 controller 16 17 import ( 18 "context" 19 "fmt" 20 "time" 21 22 "github.com/cilium/cilium/api/v1/models" 23 "github.com/cilium/cilium/pkg/lock" 24 "github.com/cilium/cilium/pkg/uuid" 25 ) 26 27 var ( 28 // globalStatus is the global status of all controllers 29 globalStatus = NewManager() 30 ) 31 32 type controllerMap map[string]*Controller 33 34 // Manager is a list of controllers 35 type Manager struct { 36 controllers controllerMap 37 mutex lock.RWMutex 38 } 39 40 // NewManager allocates a new manager 41 func NewManager() *Manager { 42 return &Manager{ 43 controllers: controllerMap{}, 44 } 45 } 46 47 // GetGlobalStatus returns the status of all controllers 48 func GetGlobalStatus() models.ControllerStatuses { 49 return globalStatus.GetStatusModel() 50 } 51 52 // UpdateController installs or updates a controller in the manager. A 53 // controller is identified by its name. If a controller with the name already 54 // exists, the controller will be shut down and replaced with the provided 55 // controller. Updating a controller will cause the DoFunc to be run 56 // immediately regardless of any previous conditions. It will also cause any 57 // statistics to be reset. 58 func (m *Manager) UpdateController(name string, params ControllerParams) *Controller { 59 start := time.Now() 60 61 // ensure the callbacks are valid 62 if params.DoFunc == nil { 63 params.DoFunc = func(ctx context.Context) error { 64 return undefinedDoFunc(name) 65 } 66 } 67 if params.StopFunc == nil { 68 params.StopFunc = NoopFunc 69 } 70 71 m.mutex.Lock() 72 73 if m.controllers == nil { 74 m.controllers = controllerMap{} 75 } 76 77 ctrl, exists := m.controllers[name] 78 if exists { 79 m.mutex.Unlock() 80 81 ctrl.getLogger().Debug("Updating existing controller") 82 ctrl.mutex.Lock() 83 ctrl.updateParamsLocked(params) 84 ctrl.mutex.Unlock() 85 86 // Notify the goroutine of the params update. 87 select { 88 case ctrl.update <- struct{}{}: 89 default: 90 } 91 92 ctrl.getLogger().Debug("Controller update time: ", time.Since(start)) 93 } else { 94 ctrl = &Controller{ 95 name: name, 96 uuid: uuid.NewUUID().String(), 97 stop: make(chan struct{}), 98 update: make(chan struct{}, 1), 99 terminated: make(chan struct{}), 100 } 101 ctrl.updateParamsLocked(params) 102 ctrl.getLogger().Debug("Starting new controller") 103 104 ctrl.ctxDoFunc, ctrl.cancelDoFunc = context.WithCancel(context.Background()) 105 m.controllers[ctrl.name] = ctrl 106 m.mutex.Unlock() 107 108 globalStatus.mutex.Lock() 109 globalStatus.controllers[ctrl.uuid] = ctrl 110 globalStatus.mutex.Unlock() 111 112 go ctrl.runController() 113 } 114 115 return ctrl 116 } 117 118 func (m *Manager) removeController(ctrl *Controller) { 119 ctrl.stopController() 120 delete(m.controllers, ctrl.name) 121 122 globalStatus.mutex.Lock() 123 delete(globalStatus.controllers, ctrl.uuid) 124 globalStatus.mutex.Unlock() 125 126 ctrl.getLogger().Debug("Removed controller") 127 } 128 129 func (m *Manager) lookup(name string) *Controller { 130 m.mutex.RLock() 131 defer m.mutex.RUnlock() 132 133 if c, ok := m.controllers[name]; ok { 134 return c 135 } 136 137 return nil 138 } 139 140 func (m *Manager) removeAndReturnController(name string) (*Controller, error) { 141 m.mutex.Lock() 142 defer m.mutex.Unlock() 143 144 if m.controllers == nil { 145 return nil, fmt.Errorf("empty controller map") 146 } 147 148 oldCtrl, ok := m.controllers[name] 149 if !ok { 150 return nil, fmt.Errorf("unable to find controller %s", name) 151 } 152 153 m.removeController(oldCtrl) 154 155 return oldCtrl, nil 156 } 157 158 // RemoveController stops and removes a controller from the manager. If DoFunc 159 // is currently running, DoFunc is allowed to complete in the background. 160 func (m *Manager) RemoveController(name string) error { 161 _, err := m.removeAndReturnController(name) 162 return err 163 } 164 165 // RemoveControllerAndWait stops and removes a controller using 166 // RemoveController() and then waits for it to run to completion. 167 func (m *Manager) RemoveControllerAndWait(name string) error { 168 oldCtrl, err := m.removeAndReturnController(name) 169 if err == nil { 170 <-oldCtrl.terminated 171 } 172 173 return err 174 } 175 176 // TerminationChannel returns a channel that is closed after the controller has 177 // been terminated 178 func (m *Manager) TerminationChannel(name string) chan struct{} { 179 if c := m.lookup(name); c != nil { 180 return c.terminated 181 } 182 183 c := make(chan struct{}) 184 close(c) 185 return c 186 } 187 188 func (m *Manager) removeAll() []*Controller { 189 ctrls := []*Controller{} 190 191 m.mutex.Lock() 192 defer m.mutex.Unlock() 193 194 if m.controllers == nil { 195 return ctrls 196 } 197 198 for _, ctrl := range m.controllers { 199 m.removeController(ctrl) 200 ctrls = append(ctrls, ctrl) 201 } 202 203 return ctrls 204 } 205 206 // RemoveAll stops and removes all controllers of the manager 207 func (m *Manager) RemoveAll() { 208 m.removeAll() 209 } 210 211 // RemoveAllAndWait stops and removes all controllers of the manager and then 212 // waits for all controllers to exit 213 func (m *Manager) RemoveAllAndWait() { 214 ctrls := m.removeAll() 215 for _, ctrl := range ctrls { 216 <-ctrl.terminated 217 } 218 } 219 220 // GetStatusModel returns the status of all controllers as models.ControllerStatuses 221 func (m *Manager) GetStatusModel() models.ControllerStatuses { 222 // Create a copy of pointers to current controller so we can unlock the 223 // manager mutex quickly again 224 controllers := controllerMap{} 225 m.mutex.RLock() 226 for key, c := range m.controllers { 227 controllers[key] = c 228 } 229 m.mutex.RUnlock() 230 231 statuses := models.ControllerStatuses{} 232 for _, c := range controllers { 233 statuses = append(statuses, c.GetStatusModel()) 234 } 235 236 return statuses 237 } 238 239 // FakeManager returns a fake controller manager with the specified number of 240 // failing controllers. The returned manager is identical in any regard except 241 // for internal pointers. 242 func FakeManager(failingControllers int) *Manager { 243 m := &Manager{ 244 controllers: controllerMap{}, 245 } 246 247 for i := 0; i < failingControllers; i++ { 248 ctrl := &Controller{ 249 name: fmt.Sprintf("controller-%d", i), 250 uuid: fmt.Sprintf("%d", i), 251 stop: make(chan struct{}), 252 update: make(chan struct{}, 1), 253 terminated: make(chan struct{}), 254 lastError: fmt.Errorf("controller failed"), 255 failureCount: 1, 256 consecutiveErrors: 1, 257 } 258 259 ctrl.ctxDoFunc, ctrl.cancelDoFunc = context.WithCancel(context.Background()) 260 m.controllers[ctrl.name] = ctrl 261 } 262 263 return m 264 }