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