github.com/cilium/cilium@v1.16.2/pkg/controller/manager.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package controller
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  
    10  	"github.com/go-openapi/strfmt"
    11  	"github.com/google/uuid"
    12  
    13  	"github.com/cilium/cilium/api/v1/models"
    14  	"github.com/cilium/cilium/pkg/lock"
    15  	"github.com/cilium/cilium/pkg/option"
    16  	"github.com/cilium/cilium/pkg/time"
    17  )
    18  
    19  var (
    20  	// globalStatus is the global status of all controllers
    21  	globalStatus = NewManager()
    22  )
    23  
    24  type controllerMap map[string]*managedController
    25  
    26  // Manager is a list of controllers
    27  type Manager struct {
    28  	controllers controllerMap
    29  	mutex       lock.RWMutex
    30  }
    31  
    32  // NewManager allocates a new manager
    33  func NewManager() *Manager {
    34  	return &Manager{
    35  		controllers: controllerMap{},
    36  	}
    37  }
    38  
    39  // GetGlobalStatus returns the status of all controllers
    40  func GetGlobalStatus() models.ControllerStatuses {
    41  	return globalStatus.GetStatusModel()
    42  }
    43  
    44  // UpdateController installs or updates a controller in the
    45  // manager. A controller is primarily identified by its name.
    46  // If a controller with the name already exists, the controller
    47  // will be shut down and replaced with the provided controller.
    48  //
    49  // Updating a controller will cause the DoFunc to be run immediately regardless
    50  // of any previous conditions. It will also cause any statistics to be reset.
    51  func (m *Manager) UpdateController(name string, params ControllerParams) {
    52  	m.updateController(name, params)
    53  }
    54  
    55  func (m *Manager) updateController(name string, params ControllerParams) *managedController {
    56  	start := time.Now()
    57  
    58  	m.mutex.Lock()
    59  	defer m.mutex.Unlock()
    60  
    61  	if m.controllers == nil {
    62  		m.controllers = controllerMap{}
    63  	}
    64  
    65  	if params.Group.Name == "" {
    66  		log.Errorf(
    67  			"Controller initialized with unpopulated group information. " +
    68  				"Metrics will not be exported for this controller.")
    69  	}
    70  
    71  	ctrl := m.lookupLocked(name)
    72  	if ctrl != nil {
    73  		ctrl.getLogger().Debug("Updating existing controller")
    74  		ctrl.updateParamsLocked(params)
    75  
    76  		// Notify the goroutine of the params update.
    77  		select {
    78  		case ctrl.update <- ctrl.params:
    79  		default:
    80  		}
    81  
    82  		ctrl.getLogger().Debug("Controller update time: ", time.Since(start))
    83  	} else {
    84  		return m.createControllerLocked(name, params)
    85  	}
    86  
    87  	return ctrl
    88  }
    89  
    90  func (m *Manager) createControllerLocked(name string, params ControllerParams) *managedController {
    91  	ctrl := &managedController{
    92  		controller: controller{
    93  			name:       name,
    94  			group:      params.Group,
    95  			uuid:       uuid.New().String(),
    96  			stop:       make(chan struct{}),
    97  			update:     make(chan ControllerParams, 1),
    98  			trigger:    make(chan struct{}, 1),
    99  			terminated: make(chan struct{}),
   100  		},
   101  	}
   102  	ctrl.updateParamsLocked(params)
   103  	ctrl.getLogger().Debug("Starting new controller")
   104  
   105  	m.controllers[ctrl.name] = ctrl
   106  
   107  	globalStatus.mutex.Lock()
   108  	globalStatus.controllers[ctrl.uuid] = ctrl
   109  	globalStatus.mutex.Unlock()
   110  
   111  	go ctrl.runController(ctrl.params)
   112  	return ctrl
   113  }
   114  
   115  // CreateController installs a new controller in the
   116  // manager.  If a controller with the name already exists
   117  // this method returns false without triggering, otherwise
   118  // creates the controller and runs it immediately.
   119  func (m *Manager) CreateController(name string, params ControllerParams) bool {
   120  	m.mutex.Lock()
   121  	defer m.mutex.Unlock()
   122  
   123  	if m.controllers != nil {
   124  		if ctrl := m.lookupLocked(name); ctrl != nil {
   125  			return false
   126  		}
   127  	} else {
   128  		m.controllers = controllerMap{}
   129  	}
   130  	m.createControllerLocked(name, params)
   131  	return true
   132  }
   133  
   134  func (m *Manager) removeController(ctrl *managedController) {
   135  	ctrl.stopController()
   136  	delete(m.controllers, ctrl.name)
   137  
   138  	globalStatus.mutex.Lock()
   139  	delete(globalStatus.controllers, ctrl.uuid)
   140  	globalStatus.mutex.Unlock()
   141  
   142  	ctrl.getLogger().Debug("Removed controller")
   143  }
   144  
   145  func (m *Manager) lookup(name string) *managedController {
   146  	m.mutex.RLock()
   147  	defer m.mutex.RUnlock()
   148  	return m.lookupLocked(name)
   149  }
   150  
   151  func (m *Manager) lookupLocked(name string) *managedController {
   152  	if c, ok := m.controllers[name]; ok {
   153  		return c
   154  	}
   155  	return nil
   156  }
   157  
   158  func (m *Manager) removeAndReturnController(name string) (*managedController, error) {
   159  	m.mutex.Lock()
   160  	defer m.mutex.Unlock()
   161  
   162  	if m.controllers == nil {
   163  		return nil, fmt.Errorf("empty controller map")
   164  	}
   165  
   166  	oldCtrl := m.lookupLocked(name)
   167  	if oldCtrl == nil {
   168  		return nil, fmt.Errorf("unable to find controller %s", name)
   169  	}
   170  
   171  	m.removeController(oldCtrl)
   172  
   173  	return oldCtrl, nil
   174  }
   175  
   176  // RemoveController stops and removes a controller from the manager. If DoFunc
   177  // is currently running, DoFunc is allowed to complete in the background.
   178  func (m *Manager) RemoveController(name string) error {
   179  	_, err := m.removeAndReturnController(name)
   180  	return err
   181  }
   182  
   183  // RemoveControllerAndWait stops and removes a controller using
   184  // RemoveController() and then waits for it to run to completion.
   185  func (m *Manager) RemoveControllerAndWait(name string) error {
   186  	oldCtrl, err := m.removeAndReturnController(name)
   187  	if err == nil {
   188  		<-oldCtrl.terminated
   189  	}
   190  
   191  	return err
   192  }
   193  
   194  func (m *Manager) removeAll() []*managedController {
   195  	ctrls := []*managedController{}
   196  
   197  	m.mutex.Lock()
   198  	defer m.mutex.Unlock()
   199  
   200  	if m.controllers == nil {
   201  		return ctrls
   202  	}
   203  
   204  	for _, ctrl := range m.controllers {
   205  		m.removeController(ctrl)
   206  		ctrls = append(ctrls, ctrl)
   207  	}
   208  
   209  	return ctrls
   210  }
   211  
   212  // RemoveAll stops and removes all controllers of the manager
   213  func (m *Manager) RemoveAll() {
   214  	m.removeAll()
   215  }
   216  
   217  // RemoveAllAndWait stops and removes all controllers of the manager and then
   218  // waits for all controllers to exit
   219  func (m *Manager) RemoveAllAndWait() {
   220  	ctrls := m.removeAll()
   221  	for _, ctrl := range ctrls {
   222  		<-ctrl.terminated
   223  	}
   224  }
   225  
   226  // GetStatusModel returns the status of all controllers as models.ControllerStatuses
   227  func (m *Manager) GetStatusModel() models.ControllerStatuses {
   228  	// Create a copy of pointers to current controller so we can unlock the
   229  	// manager mutex quickly again
   230  	controllers := controllerMap{}
   231  	m.mutex.RLock()
   232  	for key, c := range m.controllers {
   233  		controllers[key] = c
   234  	}
   235  	m.mutex.RUnlock()
   236  
   237  	statuses := models.ControllerStatuses{}
   238  	for _, c := range controllers {
   239  		statuses = append(statuses, c.GetStatusModel())
   240  	}
   241  
   242  	return statuses
   243  }
   244  
   245  // TriggerController triggers the controller with the specified name.
   246  func (m *Manager) TriggerController(name string) {
   247  	ctrl := m.lookup(name)
   248  	if ctrl == nil {
   249  		return
   250  	}
   251  
   252  	select {
   253  	case ctrl.trigger <- struct{}{}:
   254  	default:
   255  	}
   256  }
   257  
   258  type managedController struct {
   259  	controller
   260  
   261  	params       ControllerParams
   262  	cancelDoFunc context.CancelFunc
   263  }
   264  
   265  // updateParamsLocked sanitizes and sets the controller's parameters.
   266  //
   267  // If the RunInterval exceeds ControllerMaxInterval, it will be capped.
   268  //
   269  // Manager's mutex must be held
   270  func (c *managedController) updateParamsLocked(params ControllerParams) {
   271  	// ensure the callbacks are valid
   272  	if params.DoFunc == nil {
   273  		params.DoFunc = func(ctx context.Context) error {
   274  			return undefinedDoFunc(c.name)
   275  		}
   276  	}
   277  	if params.StopFunc == nil {
   278  		params.StopFunc = NoopFunc
   279  	}
   280  
   281  	// Enforce max controller interval
   282  	maxInterval := time.Duration(option.Config.MaxControllerInterval) * time.Second
   283  	if maxInterval > 0 && params.RunInterval > maxInterval {
   284  		c.getLogger().Infof("Limiting interval to %s", maxInterval)
   285  		params.RunInterval = maxInterval
   286  	}
   287  
   288  	// Save current context on update if not canceling
   289  	ctx := c.params.Context
   290  	// Check if the current context needs to be cancelled
   291  	if c.params.CancelDoFuncOnUpdate && c.cancelDoFunc != nil {
   292  		c.cancelDoFunc()
   293  		c.params.Context = nil
   294  	}
   295  
   296  	// (re)set the context as the previous might have been cancelled
   297  	if c.params.Context == nil {
   298  		if params.Context == nil {
   299  			ctx, c.cancelDoFunc = context.WithCancel(context.Background())
   300  		} else {
   301  			ctx, c.cancelDoFunc = context.WithCancel(params.Context)
   302  		}
   303  	}
   304  
   305  	c.params = params
   306  	c.params.Context = ctx
   307  }
   308  
   309  func (c *managedController) stopController() {
   310  	if c.cancelDoFunc != nil {
   311  		c.cancelDoFunc()
   312  	}
   313  
   314  	close(c.stop)
   315  }
   316  
   317  // GetStatusModel returns a models.ControllerStatus representing the
   318  // controller's configuration & status
   319  func (c *managedController) GetStatusModel() *models.ControllerStatus {
   320  	c.mutex.RLock()
   321  	defer c.mutex.RUnlock()
   322  
   323  	status := &models.ControllerStatus{
   324  		Name: c.name,
   325  		UUID: strfmt.UUID(c.uuid),
   326  		Configuration: &models.ControllerStatusConfiguration{
   327  			ErrorRetry:     !c.params.NoErrorRetry,
   328  			ErrorRetryBase: strfmt.Duration(c.params.ErrorRetryBaseDuration),
   329  			Interval:       strfmt.Duration(c.params.RunInterval),
   330  		},
   331  		Status: &models.ControllerStatusStatus{
   332  			SuccessCount:            int64(c.successCount),
   333  			LastSuccessTimestamp:    strfmt.DateTime(c.lastSuccessStamp),
   334  			FailureCount:            int64(c.failureCount),
   335  			LastFailureTimestamp:    strfmt.DateTime(c.lastErrorStamp),
   336  			ConsecutiveFailureCount: int64(c.consecutiveErrors),
   337  		},
   338  	}
   339  
   340  	if c.lastError != nil {
   341  		status.Status.LastFailureMsg = c.lastError.Error()
   342  	}
   343  
   344  	return status
   345  }