github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/stateengine.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package overlord
    21  
    22  import (
    23  	"fmt"
    24  	"sync"
    25  
    26  	"github.com/snapcore/snapd/logger"
    27  	"github.com/snapcore/snapd/overlord/state"
    28  )
    29  
    30  // StateManager is implemented by types responsible for observing
    31  // the system and manipulating it to reflect the desired state.
    32  type StateManager interface {
    33  	// Ensure forces a complete evaluation of the current state.
    34  	// See StateEngine.Ensure for more details.
    35  	Ensure() error
    36  }
    37  
    38  // StateStarterUp is optionally implemented by StateManager that have expensive
    39  // initialization to perform before the main Overlord loop.
    40  type StateStarterUp interface {
    41  	// StartUp asks manager to perform any expensive initialization.
    42  	StartUp() error
    43  }
    44  
    45  // StateWaiter is optionally implemented by StateManagers that have running
    46  // activities that can be waited.
    47  type StateWaiter interface {
    48  	// Wait asks manager to wait for all running activities to
    49  	// finish.
    50  	Wait()
    51  }
    52  
    53  // StateStopper is optionally implemented by StateManagers that have
    54  // running activities that can be terminated.
    55  type StateStopper interface {
    56  	// Stop asks the manager to terminate all activities running
    57  	// concurrently.  It must not return before these activities
    58  	// are finished.
    59  	Stop()
    60  }
    61  
    62  // StateEngine controls the dispatching of state changes to state managers.
    63  //
    64  // Most of the actual work performed by the state engine is in fact done
    65  // by the individual managers registered. These managers must be able to
    66  // cope with Ensure calls in any order, coordinating among themselves
    67  // solely via the state.
    68  type StateEngine struct {
    69  	state     *state.State
    70  	stopped   bool
    71  	startedUp bool
    72  	// managers in use
    73  	mgrLock  sync.Mutex
    74  	managers []StateManager
    75  }
    76  
    77  // NewStateEngine returns a new state engine.
    78  func NewStateEngine(s *state.State) *StateEngine {
    79  	return &StateEngine{
    80  		state: s,
    81  	}
    82  }
    83  
    84  // State returns the current system state.
    85  func (se *StateEngine) State() *state.State {
    86  	return se.state
    87  }
    88  
    89  type startupError struct {
    90  	errs []error
    91  }
    92  
    93  func (e *startupError) Error() string {
    94  	return fmt.Sprintf("state startup errors: %v", e.errs)
    95  }
    96  
    97  // StartUp asks all managers to perform any expensive initialization. It is a noop after the first invocation.
    98  func (se *StateEngine) StartUp() error {
    99  	se.mgrLock.Lock()
   100  	defer se.mgrLock.Unlock()
   101  	if se.startedUp {
   102  		return nil
   103  	}
   104  	se.startedUp = true
   105  	var errs []error
   106  	for _, m := range se.managers {
   107  		if starterUp, ok := m.(StateStarterUp); ok {
   108  			err := starterUp.StartUp()
   109  			if err != nil {
   110  				errs = append(errs, err)
   111  			}
   112  		}
   113  	}
   114  	if len(errs) != 0 {
   115  		return &startupError{errs}
   116  	}
   117  	return nil
   118  }
   119  
   120  type ensureError struct {
   121  	errs []error
   122  }
   123  
   124  func (e *ensureError) Error() string {
   125  	return fmt.Sprintf("state ensure errors: %v", e.errs)
   126  }
   127  
   128  // Ensure asks every manager to ensure that they are doing the necessary
   129  // work to put the current desired system state in place by calling their
   130  // respective Ensure methods.
   131  //
   132  // Managers must evaluate the desired state completely when they receive
   133  // that request, and report whether they found any critical issues. They
   134  // must not perform long running activities during that operation, though.
   135  // These should be performed in properly tracked changes and tasks.
   136  func (se *StateEngine) Ensure() error {
   137  	se.mgrLock.Lock()
   138  	defer se.mgrLock.Unlock()
   139  	if !se.startedUp {
   140  		return fmt.Errorf("state engine skipped startup")
   141  	}
   142  	if se.stopped {
   143  		return fmt.Errorf("state engine already stopped")
   144  	}
   145  	var errs []error
   146  	for _, m := range se.managers {
   147  		err := m.Ensure()
   148  		if err != nil {
   149  			logger.Noticef("state ensure error: %v", err)
   150  			errs = append(errs, err)
   151  		}
   152  	}
   153  	if len(errs) != 0 {
   154  		return &ensureError{errs}
   155  	}
   156  	return nil
   157  }
   158  
   159  // AddManager adds the provided manager to take part in state operations.
   160  func (se *StateEngine) AddManager(m StateManager) {
   161  	se.mgrLock.Lock()
   162  	defer se.mgrLock.Unlock()
   163  	se.managers = append(se.managers, m)
   164  }
   165  
   166  // Wait waits for all managers current activities.
   167  func (se *StateEngine) Wait() {
   168  	se.mgrLock.Lock()
   169  	defer se.mgrLock.Unlock()
   170  	if se.stopped {
   171  		return
   172  	}
   173  	for _, m := range se.managers {
   174  		if waiter, ok := m.(StateWaiter); ok {
   175  			waiter.Wait()
   176  		}
   177  	}
   178  }
   179  
   180  // Stop asks all managers to terminate activities running concurrently.
   181  func (se *StateEngine) Stop() {
   182  	se.mgrLock.Lock()
   183  	defer se.mgrLock.Unlock()
   184  	if se.stopped {
   185  		return
   186  	}
   187  	for _, m := range se.managers {
   188  		if stopper, ok := m.(StateStopper); ok {
   189  			stopper.Stop()
   190  		}
   191  	}
   192  	se.stopped = true
   193  }