github.com/elfadel/cilium@v1.6.12/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  }