-
Notifications
You must be signed in to change notification settings - Fork 303
/
live_update.go
145 lines (124 loc) · 4 KB
/
live_update.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
139
140
141
142
143
144
145
package model
import (
"github.com/pkg/errors"
)
// Specifies how to update a running container.
// 0. If any paths specified in a FallBackOn step have changed, fall back to an image build
// (i.e. don't do a LiveUpdate)
// 1. If there are Sync steps in `Steps`, files will be synced as specified.
// 2. Any time we sync one or more files, all Run and RestartContainer steps will be evaluated.
type LiveUpdate struct {
Steps []LiveUpdateStep
BaseDir string // directory where the LiveUpdate was initialized (we'll use this to eval. any relative paths)
}
func NewLiveUpdate(steps []LiveUpdateStep, baseDir string) (LiveUpdate, error) {
if len(steps) == 0 {
return LiveUpdate{}, nil
}
// Check that all FallBackOn steps come at the beginning
// (Technically could do this in the loop below, but it's
// easier to reason about/modify this way.)
seenNonFallBackStep := false
for _, step := range steps {
switch step.(type) {
case LiveUpdateFallBackOnStep:
if seenNonFallBackStep {
return LiveUpdate{}, errors.New("all fall_back_on steps must precede all other steps")
}
default:
seenNonFallBackStep = true
}
}
seenRunStep := false
for i, step := range steps {
switch step.(type) {
case LiveUpdateSyncStep:
if seenRunStep {
return LiveUpdate{}, errors.New("all sync steps must precede all run steps")
}
case LiveUpdateRunStep:
seenRunStep = true
case LiveUpdateRestartContainerStep:
if i != len(steps)-1 {
return LiveUpdate{}, errors.New("restart container is only valid as the last step")
}
}
}
return LiveUpdate{Steps: steps, BaseDir: baseDir}, nil
}
func (lu LiveUpdate) Empty() bool { return len(lu.Steps) == 0 }
type LiveUpdateStep interface {
liveUpdateStep()
}
// Specifies that changes to any of the given files should cause the builder to fall back (i.e. do a full image build)
type LiveUpdateFallBackOnStep struct {
Files []string
}
func (l LiveUpdateFallBackOnStep) liveUpdateStep() {}
// Specifies that changes to local path `Source` should be synced to container path `Dest`
type LiveUpdateSyncStep struct {
Source, Dest string
}
func (l LiveUpdateSyncStep) liveUpdateStep() {}
func (l LiveUpdateSyncStep) toSync() Sync {
return Sync{
LocalPath: l.Source,
ContainerPath: l.Dest,
}
}
// Specifies that `Command` should be executed when any files in `Sync` steps have changed
// If `Trigger` is non-empty, `Command` will only be executed when the local paths of changed files covered by
// at least one `Sync` match one of `PathSet.Paths` (evaluated relative to `PathSet.BaseDirectory`.
type LiveUpdateRunStep struct {
Command Cmd
Triggers PathSet
}
func (l LiveUpdateRunStep) liveUpdateStep() {}
func (l LiveUpdateRunStep) toRun() Run {
return Run{Cmd: l.Command, Triggers: l.Triggers}
}
// Specifies that the container should be restarted when any files in `Sync` steps have changed.
type LiveUpdateRestartContainerStep struct{}
func (l LiveUpdateRestartContainerStep) liveUpdateStep() {}
// FallBackOnFiles returns a PathSet of files which, if any have changed, indicate
// that we should fall back to an image build.
func (lu LiveUpdate) FallBackOnFiles() PathSet {
var files []string
for _, step := range lu.Steps {
switch step := step.(type) {
case LiveUpdateFallBackOnStep:
files = append(files, step.Files...)
}
}
return NewPathSet(files, lu.BaseDir)
}
func (lu LiveUpdate) SyncSteps() []Sync {
var syncs []Sync
for _, step := range lu.Steps {
switch step := step.(type) {
case LiveUpdateSyncStep:
syncs = append(syncs, step.toSync())
}
}
return syncs
}
func (lu LiveUpdate) RunSteps() []Run {
var runs []Run
for _, step := range lu.Steps {
switch step := step.(type) {
case LiveUpdateRunStep:
runs = append(runs, step.toRun())
}
}
return runs
}
func (lu LiveUpdate) ShouldRestart() bool {
if len(lu.Steps) > 0 {
// Currently we require that the Restart step, if present, must be the last step.
last := lu.Steps[len(lu.Steps)-1]
if _, ok := last.(LiveUpdateRestartContainerStep); ok {
return true
}
}
return false
}