-
Notifications
You must be signed in to change notification settings - Fork 288
/
build_result.go
205 lines (172 loc) · 5.04 KB
/
build_result.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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package store
import (
"fmt"
"sort"
"github.com/docker/distribution/reference"
"github.com/windmilleng/tilt/internal/container"
"github.com/windmilleng/tilt/internal/k8s"
"github.com/windmilleng/tilt/internal/model"
)
// The results of a successful build.
type BuildResult struct {
// The name+tag of the image that the pod is running.
//
// The tag is derived from a content-addressable digest.
Image reference.NamedTagged
// If this build was a container build, containerID we built on top of
ContainerID container.ID
// Some of our build engines replace the files in-place, rather
// than building a new image. This captures how much the code
// running on-pod has diverged from the original image.
FilesReplacedSet map[string]bool
}
func (b BuildResult) IsEmpty() bool {
return b.Image == nil
}
func (b BuildResult) HasImage() bool {
return b.Image != nil
}
// Clone the build result and add new replaced files.
// Does not do a deep clone of the underlying entities.
func (b BuildResult) ShallowCloneForContainerUpdate(filesReplacedSet map[string]bool) BuildResult {
result := BuildResult{}
result.Image = b.Image
newSet := make(map[string]bool, len(b.FilesReplacedSet)+len(filesReplacedSet))
for k, v := range b.FilesReplacedSet {
newSet[k] = v
}
for k, v := range filesReplacedSet {
newSet[k] = v
}
result.FilesReplacedSet = newSet
return result
}
type BuildResultSet map[model.TargetID]BuildResult
func (set BuildResultSet) AsOneResult() BuildResult {
if len(set) == 1 {
for _, result := range set {
return result
}
}
return BuildResult{}
}
// The state of the system since the last successful build.
// This data structure should be considered immutable.
// All methods that return a new BuildState should first clone the existing build state.
type BuildState struct {
// The last successful build.
LastResult BuildResult
// Files changed since the last result was build.
// This must be liberal: it's ok if this has too many files, but not ok if it has too few.
FilesChangedSet map[string]bool
DeployInfo DeployInfo
}
func NewBuildState(result BuildResult, files []string) BuildState {
set := make(map[string]bool, len(files))
for _, f := range files {
set[f] = true
}
return BuildState{
LastResult: result,
FilesChangedSet: set,
}
}
func (b BuildState) WithDeployTarget(d DeployInfo) BuildState {
b.DeployInfo = d
return b
}
func (b BuildState) LastImageAsString() string {
img := b.LastResult.Image
if img == nil {
return ""
}
return img.String()
}
// Return the files changed since the last result in sorted order.
// The sorting helps ensure that this is deterministic, both for testing
// and for deterministic builds.
func (b BuildState) FilesChanged() []string {
result := make([]string, 0, len(b.FilesChangedSet))
for file, _ := range b.FilesChangedSet {
result = append(result, file)
}
sort.Strings(result)
return result
}
// Return the files changed since the last result's image in sorted order.
// The sorting helps ensure that this is deterministic, both for testing
// and for deterministic builds.
// Errors if there was no last result image.
func (b BuildState) FilesChangedSinceLastResultImage() ([]string, error) {
if !b.LastResult.HasImage() {
return nil, fmt.Errorf("No image in last result")
}
cSet := b.FilesChangedSet
rSet := b.LastResult.FilesReplacedSet
sum := make(map[string]bool, len(cSet)+len(rSet))
for k, v := range cSet {
sum[k] = v
}
for k, v := range rSet {
sum[k] = v
}
result := make([]string, 0, len(sum))
for file, _ := range sum {
result = append(result, file)
}
sort.Strings(result)
return result, nil
}
// A build state is empty if there are no previous results.
func (b BuildState) IsEmpty() bool {
return b.LastResult.IsEmpty()
}
func (b BuildState) HasImage() bool {
return b.LastResult.HasImage()
}
type BuildStateSet map[model.TargetID]BuildState
func (set BuildStateSet) Empty() bool {
return len(set) == 0
}
func (set BuildStateSet) FilesChanged() []string {
resultMap := map[string]bool{}
for _, state := range set {
for k := range state.FilesChangedSet {
resultMap[k] = true
}
}
result := make([]string, 0, len(resultMap))
for k := range resultMap {
result = append(result, k)
}
sort.Strings(result)
return result
}
// The information we need to find a ready container.
type DeployInfo struct {
PodID k8s.PodID
ContainerID container.ID
ContainerName container.Name
Namespace k8s.Namespace
}
func (d DeployInfo) Empty() bool {
return d == DeployInfo{}
}
// Check to see if there's a single, unambiguous Ready container
// in the given PodSet. If so, create a DeployInfo for that container.
func NewDeployInfo(podSet PodSet) DeployInfo {
if podSet.Len() != 1 {
return DeployInfo{}
}
pod := podSet.MostRecentPod()
if pod.PodID == "" || pod.ContainerID == "" || pod.ContainerName == "" || !pod.ContainerReady {
return DeployInfo{}
}
return DeployInfo{
PodID: pod.PodID,
ContainerID: pod.ContainerID,
ContainerName: pod.ContainerName,
Namespace: pod.Namespace,
}
}
var BuildStateClean = BuildState{}