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 }