-
Notifications
You must be signed in to change notification settings - Fork 288
/
local_container_build_and_deployer.go
95 lines (78 loc) · 3.09 KB
/
local_container_build_and_deployer.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
package engine
import (
"context"
"time"
"github.com/opentracing/opentracing-go"
"github.com/windmilleng/tilt/internal/build"
"github.com/windmilleng/tilt/internal/ignore"
"github.com/windmilleng/tilt/internal/logger"
"github.com/windmilleng/tilt/internal/model"
"github.com/windmilleng/tilt/internal/store"
"github.com/windmilleng/wmclient/pkg/analytics"
)
var _ BuildAndDeployer = &LocalContainerBuildAndDeployer{}
type LocalContainerBuildAndDeployer struct {
cu *build.ContainerUpdater
analytics analytics.Analytics
}
func NewLocalContainerBuildAndDeployer(cu *build.ContainerUpdater,
analytics analytics.Analytics) *LocalContainerBuildAndDeployer {
return &LocalContainerBuildAndDeployer{
cu: cu,
analytics: analytics,
}
}
func (cbd *LocalContainerBuildAndDeployer) BuildAndDeploy(ctx context.Context, manifest model.Manifest, state store.BuildState) (result store.BuildResult, err error) {
span, ctx := opentracing.StartSpanFromContext(ctx, "LocalContainerBuildAndDeployer-BuildAndDeploy")
span.SetTag("manifest", manifest.Name.String())
defer span.Finish()
if manifest.IsDC() {
return store.BuildResult{}, RedirectToNextBuilderf("not implemented: DC container builds")
}
startTime := time.Now()
defer func() {
cbd.analytics.Timer("build.container", time.Since(startTime), nil)
}()
// TODO(maia): put manifest.Validate() upstream if we're gonna want to call it regardless
// of implementation of BuildAndDeploy?
err = manifest.Validate()
if err != nil {
return store.BuildResult{}, WrapDontFallBackError(err)
}
// LocalContainerBuildAndDeployer doesn't support initial build
if state.IsEmpty() {
return store.BuildResult{}, RedirectToNextBuilderf("prev. build state is empty; container build does not support initial deploy")
}
image := manifest.ImageTarget
fbInfo, ok := image.BuildDetails.(model.FastBuild)
if !ok {
return store.BuildResult{}, RedirectToNextBuilderf("container build only supports FastBuilds")
}
// Otherwise, manifest has already been deployed; try to update in the running container
deployInfo := state.DeployInfo
if deployInfo.Empty() {
return store.BuildResult{}, RedirectToNextBuilderf("no deploy info")
}
cf, err := build.FilesToPathMappings(state.FilesChanged(), fbInfo.Mounts)
if err != nil {
return store.BuildResult{}, err
}
logger.Get(ctx).Infof(" → Updating container…")
boiledSteps, err := build.BoilSteps(fbInfo.Steps, cf)
if err != nil {
return store.BuildResult{}, err
}
// TODO - use PipelineState here when we actually do pipeline output for container builds
writer := logger.Get(ctx).Writer(logger.InfoLvl)
err = cbd.cu.UpdateInContainer(ctx, deployInfo.ContainerID, cf, ignore.CreateBuildContextFilter(image), boiledSteps, writer)
if err != nil {
if build.IsUserBuildFailure(err) {
return store.BuildResult{}, WrapDontFallBackError(err)
}
return store.BuildResult{}, err
}
logger.Get(ctx).Infof(" → Container updated!")
res := state.LastResult.ShallowCloneForContainerUpdate(state.FilesChangedSet)
res.ContainerID = deployInfo.ContainerID // the container we deployed on top of
return res, nil
}