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  }