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 }