github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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  
    28  	"github.com/snapcore/snapd/overlord/state"
    29  )
    30  
    31  // StateManager is implemented by types responsible for observing
    32  // the system and manipulating it to reflect the desired state.
    33  type StateManager interface {
    34  	// Ensure forces a complete evaluation of the current state.
    35  	// See StateEngine.Ensure for more details.
    36  	Ensure() error
    37  }
    38  
    39  // StateStarterUp is optionally implemented by StateManager that have expensive
    40  // initialization to perform before the main Overlord loop.
    41  type StateStarterUp interface {
    42  	// StartUp asks manager to perform any expensive initialization.
    43  	StartUp() error
    44  }
    45  
    46  // StateWaiter is optionally implemented by StateManagers that have running
    47  // activities that can be waited.
    48  type StateWaiter interface {
    49  	// Wait asks manager to wait for all running activities to
    50  	// finish.
    51  	Wait()
    52  }
    53  
    54  // StateStopper is optionally implemented by StateManagers that have
    55  // running activities that can be terminated.
    56  type StateStopper interface {
    57  	// Stop asks the manager to terminate all activities running
    58  	// concurrently.  It must not return before these activities
    59  	// are finished.
    60  	Stop()
    61  }
    62  
    63  // StateEngine controls the dispatching of state changes to state managers.
    64  //
    65  // Most of the actual work performed by the state engine is in fact done
    66  // by the individual managers registered. These managers must be able to
    67  // cope with Ensure calls in any order, coordinating among themselves
    68  // solely via the state.
    69  type StateEngine struct {
    70  	state     *state.State
    71  	stopped   bool
    72  	startedUp bool
    73  	// managers in use
    74  	mgrLock  sync.Mutex
    75  	managers []StateManager
    76  }
    77  
    78  // NewStateEngine returns a new state engine.
    79  func NewStateEngine(s *state.State) *StateEngine {
    80  	return &StateEngine{
    81  		state: s,
    82  	}
    83  }
    84  
    85  // State returns the current system state.
    86  func (se *StateEngine) State() *state.State {
    87  	return se.state
    88  }
    89  
    90  type startupError struct {
    91  	errs []error
    92  }
    93  
    94  func (e *startupError) Error() string {
    95  	return fmt.Sprintf("state startup errors: %v", e.errs)
    96  }
    97  
    98  // StartUp asks all managers to perform any expensive initialization. It is a noop after the first invocation.
    99  func (se *StateEngine) StartUp() error {
   100  	se.mgrLock.Lock()
   101  	defer se.mgrLock.Unlock()
   102  	if se.startedUp {
   103  		return nil
   104  	}
   105  	se.startedUp = true
   106  	var errs []error
   107  	for _, m := range se.managers {
   108  		if starterUp, ok := m.(StateStarterUp); ok {
   109  			err := starterUp.StartUp()
   110  			if err != nil {
   111  				errs = append(errs, err)
   112  			}
   113  		}
   114  	}
   115  	if len(errs) != 0 {
   116  		return &startupError{errs}
   117  	}
   118  	return nil
   119  }
   120  
   121  type ensureError struct {
   122  	errs []error
   123  }
   124  
   125  func (e *ensureError) Error() string {
   126  	return fmt.Sprintf("state ensure errors: %v", e.errs)
   127  }
   128  
   129  // Ensure asks every manager to ensure that they are doing the necessary
   130  // work to put the current desired system state in place by calling their
   131  // respective Ensure methods.
   132  //
   133  // Managers must evaluate the desired state completely when they receive
   134  // that request, and report whether they found any critical issues. They
   135  // must not perform long running activities during that operation, though.
   136  // These should be performed in properly tracked changes and tasks.
   137  func (se *StateEngine) Ensure() error {
   138  	se.mgrLock.Lock()
   139  	defer se.mgrLock.Unlock()
   140  	if !se.startedUp {
   141  		return fmt.Errorf("state engine skipped startup")
   142  	}
   143  	if se.stopped {
   144  		return fmt.Errorf("state engine already stopped")
   145  	}
   146  	var errs []error
   147  	for _, m := range se.managers {
   148  		err := m.Ensure()
   149  		if err != nil {
   150  			logger.Noticef("state ensure error: %v", err)
   151  			errs = append(errs, err)
   152  		}
   153  	}
   154  	if len(errs) != 0 {
   155  		return &ensureError{errs}
   156  	}
   157  	return nil
   158  }
   159  
   160  // AddManager adds the provided manager to take part in state operations.
   161  func (se *StateEngine) AddManager(m StateManager) {
   162  	se.mgrLock.Lock()
   163  	defer se.mgrLock.Unlock()
   164  	se.managers = append(se.managers, m)
   165  }
   166  
   167  // Wait waits for all managers current activities.
   168  func (se *StateEngine) Wait() {
   169  	se.mgrLock.Lock()
   170  	defer se.mgrLock.Unlock()
   171  	if se.stopped {
   172  		return
   173  	}
   174  	for _, m := range se.managers {
   175  		if waiter, ok := m.(StateWaiter); ok {
   176  			waiter.Wait()
   177  		}
   178  	}
   179  }
   180  
   181  // Stop asks all managers to terminate activities running concurrently.
   182  func (se *StateEngine) Stop() {
   183  	se.mgrLock.Lock()
   184  	defer se.mgrLock.Unlock()
   185  	if se.stopped {
   186  		return
   187  	}
   188  	for _, m := range se.managers {
   189  		if stopper, ok := m.(StateStopper); ok {
   190  			stopper.Stop()
   191  		}
   192  	}
   193  	se.stopped = true
   194  }