Skip to content

Commit cdab500

Browse files
authored
tiltfile: fix a bug where sometimes a base image wasn't pushed (#6514)
if a base image is used in ANY manifest, it needs to be pushed in all manifests. Otherwise the caching system will get confused. fixes issue #6486 Signed-off-by: Nick Santos <[email protected]>
1 parent 36fa659 commit cdab500

File tree

7 files changed

+102
-32
lines changed

7 files changed

+102
-32
lines changed

internal/cli/docker_prune.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,8 @@ func (c *dockerPruneCmd) run(ctx context.Context, args []string) error {
100100
// with all resources in a "disabled" state and rely on the API, but that's not
101101
// possible currently.
102102
func resolveImageSelectors(ctx context.Context, kCli k8s.Client, tlr *tiltfile.TiltfileLoadResult) ([]container.RefSelector, error) {
103-
for _, m := range tlr.Manifests {
104-
if err := m.InferImageProperties(); err != nil {
105-
return nil, err
106-
}
103+
if err := model.InferImageProperties(tlr.Manifests); err != nil {
104+
return nil, err
107105
}
108106

109107
var reg *v1alpha1.RegistryHosting

internal/controllers/core/tiltfile/api.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,13 @@ func updateOwnedObjects(
6262
disableSources := toDisableSources(tlr)
6363

6464
if tlr != nil {
65-
for i, m := range tlr.Manifests {
66-
// Apply the registry to the image refs.
67-
err := m.InferImageProperties()
68-
if err != nil {
69-
return err
70-
}
65+
// Apply the registry to the image refs.
66+
err := model.InferImageProperties(tlr.Manifests)
67+
if err != nil {
68+
return err
69+
}
7170

71+
for i, m := range tlr.Manifests {
7272
// Assemble the LiveUpdate selectors, connecting objects together.
7373
err = m.InferLiveUpdateSelectors()
7474
if err != nil {

internal/controllers/core/tiltfile/reconciler_test.go

+58
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"path/filepath"
88
"reflect"
9+
"sort"
910
"strconv"
1011
"testing"
1112
"time"
@@ -455,6 +456,63 @@ func TestCancelClickedBeforeLoad(t *testing.T) {
455456
require.Equal(t, "", tf.Status.Terminated.Error)
456457
}
457458

459+
func TestPushBaseImageIssue6486(t *testing.T) {
460+
f := newFixture(t)
461+
p := f.tempdir.JoinPath("Tiltfile")
462+
463+
image1 := model.MustNewImageTarget(container.MustParseSelector("image-1")).
464+
WithDockerImage(v1alpha1.DockerImageSpec{Context: f.tempdir.Path()})
465+
image2 := model.MustNewImageTarget(container.MustParseSelector("image-2")).
466+
WithDockerImage(v1alpha1.DockerImageSpec{Context: f.tempdir.Path()})
467+
image3 := model.MustNewImageTarget(container.MustParseSelector("image-3")).
468+
WithDockerImage(v1alpha1.DockerImageSpec{Context: f.tempdir.Path()}).
469+
WithImageMapDeps([]string{"image-1", "image-2"})
470+
471+
service1 := manifestbuilder.New(f.tempdir, "service-1").
472+
WithImageTargets(image1).
473+
WithK8sYAML(testyaml.Deployment("service-1", "image-1")).
474+
Build()
475+
service3 := manifestbuilder.New(f.tempdir, "service-3").
476+
WithImageTargets(image1, image2, image3).
477+
WithK8sYAML(testyaml.Deployment("service-3", "image-3")).
478+
Build()
479+
480+
f.tfl.Result = tiltfile.TiltfileLoadResult{
481+
Manifests: []model.Manifest{service1, service3},
482+
}
483+
484+
name := model.MainTiltfileManifestName.String()
485+
tf := v1alpha1.Tiltfile{
486+
ObjectMeta: metav1.ObjectMeta{
487+
Name: name,
488+
},
489+
Spec: v1alpha1.TiltfileSpec{
490+
Path: p,
491+
},
492+
}
493+
f.createAndWaitForLoaded(&tf)
494+
495+
assert.Equal(t, "", tf.Status.Terminated.Error)
496+
497+
var imageList = v1alpha1.DockerImageList{}
498+
f.List(&imageList)
499+
500+
sort.Slice(imageList.Items, func(i, j int) bool {
501+
return imageList.Items[i].Name < imageList.Items[j].Name
502+
})
503+
504+
if assert.Equal(t, 4, len(imageList.Items)) {
505+
assert.Equal(t, "service-1:image-1", imageList.Items[0].Name)
506+
assert.Equal(t, v1alpha1.ClusterImageNeedsPush, imageList.Items[0].Spec.ClusterNeeds)
507+
assert.Equal(t, "service-3:image-1", imageList.Items[1].Name)
508+
assert.Equal(t, v1alpha1.ClusterImageNeedsPush, imageList.Items[1].Spec.ClusterNeeds)
509+
assert.Equal(t, "service-3:image-2", imageList.Items[2].Name)
510+
assert.Equal(t, v1alpha1.ClusterImageNeedsBase, imageList.Items[2].Spec.ClusterNeeds)
511+
assert.Equal(t, "service-3:image-3", imageList.Items[3].Name)
512+
assert.Equal(t, v1alpha1.ClusterImageNeedsPush, imageList.Items[3].Spec.ClusterNeeds)
513+
}
514+
}
515+
458516
type testStore struct {
459517
*store.TestingStore
460518
out *bytes.Buffer

internal/engine/upper_test.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -3850,9 +3850,7 @@ func (f *testFixture) setupDCFixture() (redis, server model.Manifest) {
38503850
f.T().Fatalf("Expected two manifests. Actual: %v", tlr.Manifests)
38513851
}
38523852

3853-
for _, m := range tlr.Manifests {
3854-
require.NoError(f.t, m.InferImageProperties())
3855-
}
3853+
require.NoError(f.t, model.InferImageProperties(tlr.Manifests))
38563854

38573855
return tlr.Manifests[0], tlr.Manifests[1]
38583856
}

internal/testutils/manifestbuilder/manifestbuilder.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ func (b ManifestBuilder) Build() model.Manifest {
232232
}
233233
m = m.WithTriggerMode(b.triggerMode)
234234

235-
err := m.InferImageProperties()
235+
err := model.InferImageProperties([]model.Manifest{m})
236236
require.NoError(b.f.T(), err)
237237

238238
err = m.InferLiveUpdateSelectors()

internal/tiltfile/tiltfile_test.go

+2-10
Original file line numberDiff line numberDiff line change
@@ -5947,11 +5947,7 @@ func (f *fixture) loadAllowWarnings(args ...string) {
59475947
f.t.Fatal(err)
59485948
}
59495949
f.loadResult = tlr
5950-
5951-
for _, m := range f.loadResult.Manifests {
5952-
err := m.InferImageProperties()
5953-
require.NoError(f.t, err)
5954-
}
5950+
require.NoError(f.t, model.InferImageProperties(f.loadResult.Manifests))
59555951
}
59565952

59575953
func unusedImageWarning(unusedImage string, suggestedImages []string, configType string) string {
@@ -5993,11 +5989,7 @@ func (f *fixture) loadArgsErrString(args []string, msgs ...string) {
59935989
f.t.Fatalf("error %q does not contain string %q", errText, msg)
59945990
}
59955991
}
5996-
5997-
for _, m := range f.loadResult.Manifests {
5998-
err := m.InferImageProperties()
5999-
require.NoError(f.t, err)
6000-
}
5992+
require.NoError(f.t, model.InferImageProperties(tlr.Manifests))
60015993
}
60025994

60035995
func (f *fixture) gitInit(path string) {

pkg/model/manifest.go

+32-8
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ func (m *Manifest) ClusterName() string {
269269
}
270270

271271
// Infer image properties for each image.
272-
func (m *Manifest) InferImageProperties() error {
272+
func (m *Manifest) inferImageProperties(clusterImageNeeds func(TargetID) v1alpha1.ClusterImageNeeds) error {
273273
var deployImageIDs []TargetID
274274
if m.DeployTarget != nil {
275275
deployImageIDs = m.DeployTarget.DependencyIDs()
@@ -280,13 +280,8 @@ func (m *Manifest) InferImageProperties() error {
280280
}
281281

282282
for i, iTarget := range m.ImageTargets {
283-
// An image only needs to be pushed if it's used in-cluster.
284-
clusterNeeds := v1alpha1.ClusterImageNeedsBase
285-
if deployImageIDSet[iTarget.ID()] {
286-
clusterNeeds = v1alpha1.ClusterImageNeedsPush
287-
}
288-
289-
iTarget, err := iTarget.inferImageProperties(clusterNeeds, m.ClusterName())
283+
iTarget, err := iTarget.inferImageProperties(
284+
clusterImageNeeds(iTarget.ID()), m.ClusterName())
290285
if err != nil {
291286
return fmt.Errorf("manifest %s: %v", m.Name, err)
292287
}
@@ -660,3 +655,32 @@ func equalForBuildInvalidation(x, y interface{}) bool {
660655
ignoreRegistryFields,
661656
)
662657
}
658+
659+
// Infer image properties for each image in the manifest set.
660+
func InferImageProperties(manifests []Manifest) error {
661+
deployImageIDSet := make(map[TargetID]bool, len(manifests))
662+
for _, m := range manifests {
663+
if m.DeployTarget != nil {
664+
for _, depID := range m.DeployTarget.DependencyIDs() {
665+
deployImageIDSet[depID] = true
666+
}
667+
}
668+
}
669+
670+
// An image only needs to be pushed if it's used in-cluster.
671+
// If it needs to be pushed for one manifest, it needs to be pushed for all.
672+
// The caching system will make sure it's not pushed multiple times.
673+
clusterImageNeeds := func(id TargetID) v1alpha1.ClusterImageNeeds {
674+
if deployImageIDSet[id] {
675+
return v1alpha1.ClusterImageNeedsPush
676+
}
677+
return v1alpha1.ClusterImageNeedsBase
678+
}
679+
680+
for _, m := range manifests {
681+
if err := m.inferImageProperties(clusterImageNeeds); err != nil {
682+
return err
683+
}
684+
}
685+
return nil
686+
}

0 commit comments

Comments
 (0)