github.com/deemoprobe/k8s-first-commit@v0.0.0-20230430165612-a541f1982be3/pkg/proxy/config/config.go (about) 1 /* 2 Copyright 2014 Google Inc. All rights reserved. 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 // Config provides decoupling between various configuration sources (etcd, files,...) and 18 // the pieces that actually care about them (loadbalancer, proxy). Config takes 1 or more 19 // configuration sources and allows for incremental (add/remove) and full replace (set) 20 // changes from each of the sources, then creates a union of the configuration and provides 21 // a unified view for both service handlers as well as endpoint handlers. There is no attempt 22 // to resolve conflicts of any sort. Basic idea is that each configuration source gets a channel 23 // from the Config service and pushes updates to it via that channel. Config then keeps track of 24 // incremental & replace changes and distributes them to listeners as appropriate. 25 package config 26 27 import ( 28 "log" 29 "sync" 30 "time" 31 32 "github.com/GoogleCloudPlatform/kubernetes/pkg/api" 33 ) 34 35 type Operation int 36 37 const ( 38 SET Operation = iota 39 ADD 40 REMOVE 41 ) 42 43 // Defines an operation sent on the channel. You can add or remove single services by 44 // sending an array of size one and Op == ADD|REMOVE. For setting the state of the system 45 // to a given state for this source configuration, set Services as desired and Op to SET, 46 // which will reset the system state to that specified in this operation for this source 47 // channel. To remove all services, set Services to empty array and Op to SET 48 type ServiceUpdate struct { 49 Services []api.Service 50 Op Operation 51 } 52 53 // Defines an operation sent on the channel. You can add or remove single endpoints by 54 // sending an array of size one and Op == ADD|REMOVE. For setting the state of the system 55 // to a given state for this source configuration, set Endpoints as desired and Op to SET, 56 // which will reset the system state to that specified in this operation for this source 57 // channel. To remove all endpoints, set Endpoints to empty array and Op to SET 58 type EndpointsUpdate struct { 59 Endpoints []api.Endpoints 60 Op Operation 61 } 62 63 type ServiceConfigHandler interface { 64 // Sent when a configuration has been changed by one of the sources. This is the 65 // union of all the configuration sources. 66 OnUpdate(services []api.Service) 67 } 68 69 type EndpointsConfigHandler interface { 70 // OnUpdate gets called when endpoints configuration is changed for a given 71 // service on any of the configuration sources. An example is when a new 72 // service comes up, or when containers come up or down for an existing service. 73 OnUpdate(endpoints []api.Endpoints) 74 } 75 76 type ServiceConfig struct { 77 // Configuration sources and their lock. 78 configSourceLock sync.RWMutex 79 serviceConfigSources map[string]chan ServiceUpdate 80 endpointsConfigSources map[string]chan EndpointsUpdate 81 82 // Handlers for changes to services and endpoints and their lock. 83 handlerLock sync.RWMutex 84 serviceHandlers []ServiceConfigHandler 85 endpointHandlers []EndpointsConfigHandler 86 87 // Last known configuration for union of the sources and the locks. Map goes 88 // from each source to array of services/endpoints that have been configured 89 // through that channel. 90 configLock sync.RWMutex 91 serviceConfig map[string]map[string]api.Service 92 endpointConfig map[string]map[string]api.Endpoints 93 94 // Channel that service configuration source listeners use to signal of new 95 // configurations. 96 // Value written is the source of the change. 97 serviceNotifyChannel chan string 98 99 // Channel that endpoint configuration source listeners use to signal of new 100 // configurations. 101 // Value written is the source of the change. 102 endpointsNotifyChannel chan string 103 } 104 105 func NewServiceConfig() ServiceConfig { 106 config := ServiceConfig{ 107 serviceConfigSources: make(map[string]chan ServiceUpdate), 108 endpointsConfigSources: make(map[string]chan EndpointsUpdate), 109 serviceHandlers: make([]ServiceConfigHandler, 10), 110 endpointHandlers: make([]EndpointsConfigHandler, 10), 111 serviceConfig: make(map[string]map[string]api.Service), 112 endpointConfig: make(map[string]map[string]api.Endpoints), 113 serviceNotifyChannel: make(chan string), 114 endpointsNotifyChannel: make(chan string), 115 } 116 go config.Run() 117 return config 118 } 119 120 func (impl *ServiceConfig) Run() { 121 log.Printf("Starting the config Run loop") 122 for { 123 select { 124 case source := <-impl.serviceNotifyChannel: 125 log.Printf("Got new service configuration from source %s", source) 126 impl.NotifyServiceUpdate() 127 case source := <-impl.endpointsNotifyChannel: 128 log.Printf("Got new endpoint configuration from source %s", source) 129 impl.NotifyEndpointsUpdate() 130 case <-time.After(1 * time.Second): 131 } 132 } 133 } 134 135 func (impl *ServiceConfig) ServiceChannelListener(source string, listenChannel chan ServiceUpdate) { 136 // Represents the current services configuration for this channel. 137 serviceMap := make(map[string]api.Service) 138 for { 139 select { 140 case update := <-listenChannel: 141 switch update.Op { 142 case ADD: 143 log.Printf("Adding new service from source %s : %v", source, update.Services) 144 for _, value := range update.Services { 145 serviceMap[value.ID] = value 146 } 147 case REMOVE: 148 log.Printf("Removing a service %v", update) 149 for _, value := range update.Services { 150 delete(serviceMap, value.ID) 151 } 152 case SET: 153 log.Printf("Setting services %v", update) 154 // Clear the old map entries by just creating a new map 155 serviceMap = make(map[string]api.Service) 156 for _, value := range update.Services { 157 serviceMap[value.ID] = value 158 } 159 default: 160 log.Printf("Received invalid update type: %v", update) 161 continue 162 } 163 impl.configLock.Lock() 164 impl.serviceConfig[source] = serviceMap 165 impl.configLock.Unlock() 166 impl.serviceNotifyChannel <- source 167 } 168 } 169 } 170 171 func (impl *ServiceConfig) EndpointsChannelListener(source string, listenChannel chan EndpointsUpdate) { 172 endpointMap := make(map[string]api.Endpoints) 173 for { 174 select { 175 case update := <-listenChannel: 176 switch update.Op { 177 case ADD: 178 log.Printf("Adding a new endpoint %v", update) 179 for _, value := range update.Endpoints { 180 endpointMap[value.Name] = value 181 } 182 case REMOVE: 183 log.Printf("Removing an endpoint %v", update) 184 for _, value := range update.Endpoints { 185 delete(endpointMap, value.Name) 186 } 187 188 case SET: 189 log.Printf("Setting services %v", update) 190 // Clear the old map entries by just creating a new map 191 endpointMap = make(map[string]api.Endpoints) 192 for _, value := range update.Endpoints { 193 endpointMap[value.Name] = value 194 } 195 default: 196 log.Printf("Received invalid update type: %v", update) 197 continue 198 } 199 impl.configLock.Lock() 200 impl.endpointConfig[source] = endpointMap 201 impl.configLock.Unlock() 202 impl.endpointsNotifyChannel <- source 203 } 204 205 } 206 } 207 208 // GetServiceConfigurationChannel returns a channel where a configuration source 209 // can send updates of new service configurations. Multiple calls with the same 210 // source will return the same channel. This allows change and state based sources 211 // to use the same channel. Difference source names however will be treated as a 212 // union. 213 func (impl *ServiceConfig) GetServiceConfigurationChannel(source string) chan ServiceUpdate { 214 if len(source) == 0 { 215 panic("GetServiceConfigurationChannel given an empty service name") 216 } 217 impl.configSourceLock.Lock() 218 defer impl.configSourceLock.Unlock() 219 channel, exists := impl.serviceConfigSources[source] 220 if exists { 221 return channel 222 } 223 newChannel := make(chan ServiceUpdate) 224 impl.serviceConfigSources[source] = newChannel 225 go impl.ServiceChannelListener(source, newChannel) 226 return newChannel 227 } 228 229 // GetEndpointConfigurationChannel returns a channel where a configuration source 230 // can send updates of new endpoint configurations. Multiple calls with the same 231 // source will return the same channel. This allows change and state based sources 232 // to use the same channel. Difference source names however will be treated as a 233 // union. 234 func (impl *ServiceConfig) GetEndpointsConfigurationChannel(source string) chan EndpointsUpdate { 235 if len(source) == 0 { 236 panic("GetEndpointConfigurationChannel given an empty service name") 237 } 238 impl.configSourceLock.Lock() 239 defer impl.configSourceLock.Unlock() 240 channel, exists := impl.endpointsConfigSources[source] 241 if exists { 242 return channel 243 } 244 newChannel := make(chan EndpointsUpdate) 245 impl.endpointsConfigSources[source] = newChannel 246 go impl.EndpointsChannelListener(source, newChannel) 247 return newChannel 248 } 249 250 // Register ServiceConfigHandler to receive updates of changes to services. 251 func (impl *ServiceConfig) RegisterServiceHandler(handler ServiceConfigHandler) { 252 impl.handlerLock.Lock() 253 defer impl.handlerLock.Unlock() 254 for i, h := range impl.serviceHandlers { 255 if h == nil { 256 impl.serviceHandlers[i] = handler 257 return 258 } 259 } 260 // TODO(vaikas): Grow the array here instead of panic. 261 // In practice we are expecting there to be 1 handler anyways, 262 // so not a big deal for now 263 panic("Only up to 10 service handlers supported for now") 264 } 265 266 // Register ServiceConfigHandler to receive updates of changes to services. 267 func (impl *ServiceConfig) RegisterEndpointsHandler(handler EndpointsConfigHandler) { 268 impl.handlerLock.Lock() 269 defer impl.handlerLock.Unlock() 270 for i, h := range impl.endpointHandlers { 271 if h == nil { 272 impl.endpointHandlers[i] = handler 273 return 274 } 275 } 276 // TODO(vaikas): Grow the array here instead of panic. 277 // In practice we are expecting there to be 1 handler anyways, 278 // so not a big deal for now 279 panic("Only up to 10 endpoint handlers supported for now") 280 } 281 282 func (impl *ServiceConfig) NotifyServiceUpdate() { 283 services := make([]api.Service, 0) 284 impl.configLock.RLock() 285 for _, sourceServices := range impl.serviceConfig { 286 for _, value := range sourceServices { 287 services = append(services, value) 288 } 289 } 290 impl.configLock.RUnlock() 291 log.Printf("Unified configuration %+v", services) 292 impl.handlerLock.RLock() 293 handlers := impl.serviceHandlers 294 impl.handlerLock.RUnlock() 295 for _, handler := range handlers { 296 if handler != nil { 297 handler.OnUpdate(services) 298 } 299 } 300 } 301 302 func (impl *ServiceConfig) NotifyEndpointsUpdate() { 303 endpoints := make([]api.Endpoints, 0) 304 impl.configLock.RLock() 305 for _, sourceEndpoints := range impl.endpointConfig { 306 for _, value := range sourceEndpoints { 307 endpoints = append(endpoints, value) 308 } 309 } 310 impl.configLock.RUnlock() 311 log.Printf("Unified configuration %+v", endpoints) 312 impl.handlerLock.RLock() 313 handlers := impl.endpointHandlers 314 impl.handlerLock.RUnlock() 315 for _, handler := range handlers { 316 if handler != nil { 317 handler.OnUpdate(endpoints) 318 } 319 } 320 }