This repository has been archived by the owner on Nov 1, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
releaser.go
139 lines (119 loc) · 4.41 KB
/
releaser.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
package release
import (
"context"
"fmt"
"strings"
"time"
"github.com/go-kit/kit/log"
"github.com/pkg/errors"
"github.com/weaveworks/flux/resource"
"github.com/weaveworks/flux/update"
)
type Changes interface {
CalculateRelease(context.Context, update.ReleaseContext, log.Logger) ([]*update.WorkloadUpdate, update.Result, error)
ReleaseKind() update.ReleaseKind
ReleaseType() update.ReleaseType
CommitMessage(update.Result) string
}
func Release(ctx context.Context, rc *ReleaseContext, changes Changes, logger log.Logger) (results update.Result, err error) {
defer func(start time.Time) {
update.ObserveRelease(
start,
err == nil,
changes.ReleaseType(),
changes.ReleaseKind(),
)
}(time.Now())
logger = log.With(logger, "type", "release")
before, err := rc.GetAllResources(ctx)
updates, results, err := changes.CalculateRelease(ctx, rc, logger)
if err != nil {
return nil, err
}
err = ApplyChanges(ctx, rc, updates, logger)
if err != nil {
return nil, MakeReleaseError(errors.Wrap(err, "applying changes"))
}
after, err := rc.GetAllResources(ctx)
if err != nil {
return nil, MakeReleaseError(errors.Wrap(err, "loading resources after updates"))
}
err = VerifyChanges(before, updates, after)
if err != nil {
return nil, MakeReleaseError(errors.Wrap(err, "verifying changes"))
}
return results, nil
}
func ApplyChanges(ctx context.Context, rc *ReleaseContext, updates []*update.WorkloadUpdate, logger log.Logger) error {
logger.Log("updates", len(updates))
if len(updates) == 0 {
logger.Log("exit", "no images to update for services given")
return nil
}
timer := update.NewStageTimer("write_changes")
err := rc.WriteUpdates(ctx, updates)
timer.ObserveDuration()
return err
}
// VerifyChanges checks that the `after` resources are exactly the
// `before` resources with the updates applied. It destructively
// updates `before`.
func VerifyChanges(before map[string]resource.Resource, updates []*update.WorkloadUpdate, after map[string]resource.Resource) error {
timer := update.NewStageTimer("verify_changes")
defer timer.ObserveDuration()
verificationError := func(msg string, args ...interface{}) error {
return errors.Wrap(fmt.Errorf(msg, args...), "failed to verify changes")
}
for _, update := range updates {
res, ok := before[update.ResourceID.String()]
if !ok {
return verificationError("resource %q mentioned in update not found in resources", update.ResourceID.String())
}
wl, ok := res.(resource.Workload)
if !ok {
return verificationError("resource %q mentioned in update is not a workload", update.ResourceID.String())
}
for _, containerUpdate := range update.Updates {
if err := wl.SetContainerImage(containerUpdate.Container, containerUpdate.Target); err != nil {
return verificationError("updating container %q in resource %q failed: %s", containerUpdate.Container, update.ResourceID.String(), err.Error())
}
}
}
for id, afterRes := range after {
beforeRes, ok := before[id]
if !ok {
return verificationError("resource %q is new after update")
}
delete(before, id)
beforeWorkload, ok := beforeRes.(resource.Workload)
if !ok {
// the resource in question isn't a workload, so ignore it.
continue
}
afterWorkload, ok := afterRes.(resource.Workload)
if !ok {
return verificationError("resource %q is no longer a workload (Deployment or DaemonSet, or similar) after update", id)
}
beforeContainers := beforeWorkload.Containers()
afterContainers := afterWorkload.Containers()
if len(beforeContainers) != len(afterContainers) {
return verificationError("resource %q has different set of containers after update", id)
}
for i := range afterContainers {
if beforeContainers[i].Name != afterContainers[i].Name {
return verificationError("container in position %d of resource %q has a different name after update: was %q, now %q", i, id, beforeContainers[i].Name, afterContainers[i].Name)
}
if beforeContainers[i].Image != afterContainers[i].Image {
return verificationError("the image for container %q in resource %q should be %q, but is %q", beforeContainers[i].Name, id, beforeContainers[i].Image.String(), afterContainers[i].Image.String())
}
}
}
var disappeared []string
for id := range before {
disappeared = append(disappeared, fmt.Sprintf("%q", id))
}
if len(disappeared) > 0 {
return verificationError("resources {%s} present before update but not after", strings.Join(disappeared, ", "))
}
return nil
}