diff --git a/internal/container/selector.go b/internal/container/selector.go index 7f0b56a39c..ad36e2fdbd 100644 --- a/internal/container/selector.go +++ b/internal/container/selector.go @@ -1,13 +1,35 @@ package container -import "github.com/docker/distribution/reference" +import ( + "github.com/docker/distribution/reference" +) + +type MatchType int + +const ( + matchName MatchType = iota + matchExact +) type RefSelector struct { - ref reference.Named + ref reference.Named + matchType MatchType +} + +func NameSelector(ref reference.Named) RefSelector { + return NewRefSelector(reference.TrimNamed(ref)) } func NewRefSelector(ref reference.Named) RefSelector { - return RefSelector{ref: ref} + matchType := matchName + _, hasTag := ref.(reference.NamedTagged) + if hasTag { + matchType = matchExact + } + return RefSelector{ + ref: ref, + matchType: matchType, + } } func MustParseSelector(s string) RefSelector { @@ -18,18 +40,31 @@ func MustParseTaggedSelector(s string) RefSelector { return NewRefSelector(MustParseNamedTagged(s)) } +func (s RefSelector) RefsEqual(other RefSelector) bool { + return s.ref.String() == other.ref.String() +} + +func (s RefSelector) WithExactMatch() RefSelector { + s.matchType = matchExact + return s +} + func (s RefSelector) Matches(toMatch reference.Named) bool { if s.ref == nil { return false } - return toMatch.Name() == s.ref.Name() + + if s.matchType == matchName { + return toMatch.Name() == s.ref.Name() + } + return toMatch.String() == s.ref.String() } func (s RefSelector) Empty() bool { return s.ref == nil } -func (s RefSelector) Name() string { +func (s RefSelector) RefName() string { return s.ref.Name() } @@ -37,12 +72,6 @@ func (s RefSelector) AsNamedOnly() reference.Named { return reference.TrimNamed(s.ref) } -// TODO(nick): This method is only provided for legacy compatibility. -// All uses of this really should not be using the full ref. -func (s RefSelector) AsRef() reference.Named { - return s.ref -} - func (s RefSelector) String() string { if s.ref == nil { return "" diff --git a/internal/dockerfile/inject_test.go b/internal/dockerfile/inject_test.go index 2992873aee..253f5a9b2c 100644 --- a/internal/dockerfile/inject_test.go +++ b/internal/dockerfile/inject_test.go @@ -13,7 +13,7 @@ FROM gcr.io/windmill/foo ADD . . `) ref := container.MustParseNamedTagged("gcr.io/windmill/foo:deadbeef") - newDf, modified, err := InjectImageDigest(df, container.NewRefSelector(ref), ref) + newDf, modified, err := InjectImageDigest(df, container.NameSelector(ref), ref) if assert.NoError(t, err) { assert.True(t, modified) assert.Equal(t, ` @@ -29,7 +29,7 @@ FROM gcr.io/windmill/foo:v1 ADD . . `) ref := container.MustParseNamedTagged("gcr.io/windmill/foo:deadbeef") - newDf, modified, err := InjectImageDigest(df, container.NewRefSelector(ref), ref) + newDf, modified, err := InjectImageDigest(df, container.NameSelector(ref), ref) if assert.NoError(t, err) { assert.True(t, modified) assert.Equal(t, ` @@ -45,7 +45,7 @@ FROM gcr.io/windmill/bar:v1 ADD . . `) ref := container.MustParseNamedTagged("gcr.io/windmill/foo:deadbeef") - newDf, modified, err := InjectImageDigest(df, container.NewRefSelector(ref), ref) + newDf, modified, err := InjectImageDigest(df, container.NameSelector(ref), ref) if assert.NoError(t, err) { assert.False(t, modified) assert.Equal(t, df, newDf) @@ -59,7 +59,7 @@ COPY --from=gcr.io/windmill/foo /src/package.json /src/package.json ADD . . `) ref := container.MustParseNamedTagged("gcr.io/windmill/foo:deadbeef") - newDf, modified, err := InjectImageDigest(df, container.NewRefSelector(ref), ref) + newDf, modified, err := InjectImageDigest(df, container.NameSelector(ref), ref) if assert.NoError(t, err) { assert.True(t, modified) assert.Equal(t, ` @@ -77,7 +77,7 @@ COPY --from=gcr.io/windmill/foo:bar /src/package.json /src/package.json ADD . . `) ref := container.MustParseNamedTagged("gcr.io/windmill/foo:deadbeef") - newDf, modified, err := InjectImageDigest(df, container.NewRefSelector(ref), ref) + newDf, modified, err := InjectImageDigest(df, container.NameSelector(ref), ref) if assert.NoError(t, err) { assert.True(t, modified) assert.Equal(t, ` @@ -95,7 +95,7 @@ COPY --from=vandelay/common /usr/src/common/package.json /usr/src/common/yarn.lo ADD . . `) ref := container.MustParseNamedTagged("vandelay/common:deadbeef") - newDf, modified, err := InjectImageDigest(df, container.NewRefSelector(ref), ref) + newDf, modified, err := InjectImageDigest(df, container.NameSelector(ref), ref) if assert.NoError(t, err) { assert.True(t, modified) assert.Equal(t, ` @@ -118,7 +118,7 @@ ADD . . t.Fatal(err) } - modified, err := ast.InjectImageDigest(container.NewRefSelector(ref), ref) + modified, err := ast.InjectImageDigest(container.NameSelector(ref), ref) if assert.NoError(t, err) { assert.True(t, modified) @@ -133,7 +133,7 @@ ADD . . `, string(newDf)) } - modified, err = ast.InjectImageDigest(container.NewRefSelector(ref), ref) + modified, err = ast.InjectImageDigest(container.NameSelector(ref), ref) if assert.NoError(t, err) { assert.True(t, modified) diff --git a/internal/engine/image_build_and_deployer.go b/internal/engine/image_build_and_deployer.go index 507eb7bfdf..ae954caaa8 100644 --- a/internal/engine/image_build_and_deployer.go +++ b/internal/engine/image_build_and_deployer.go @@ -196,8 +196,10 @@ func (ibd *ImageBuildAndDeployer) deploy(ctx context.Context, st store.RStore, p injectedDepIDs[depID] = true if ibd.injectSynclet && needsSynclet { + injectedRefSelector := container.NewRefSelector(ref).WithExactMatch() + var sidecarInjected bool - e, sidecarInjected, err = sidecar.InjectSyncletSidecar(e, selector) + e, sidecarInjected, err = sidecar.InjectSyncletSidecar(e, injectedRefSelector) if err != nil { return err } diff --git a/internal/k8s/image.go b/internal/k8s/image.go index 505d775511..60db67ffa7 100644 --- a/internal/k8s/image.go +++ b/internal/k8s/image.go @@ -231,8 +231,8 @@ func (e K8sEntity) FindImages(imageJSONPaths []JSONPath) ([]reference.Named, err return result, nil } -func PodContainsRef(pod v1.PodSpec, ref reference.Named) (bool, error) { - cRef, err := FindImageRefMatching(pod, ref) +func PodContainsRef(pod v1.PodSpec, selector container.RefSelector) (bool, error) { + cRef, err := FindImageRefMatching(pod, selector) if err != nil { return false, err } @@ -240,22 +240,22 @@ func PodContainsRef(pod v1.PodSpec, ref reference.Named) (bool, error) { return cRef != nil, nil } -func FindImageRefMatching(pod v1.PodSpec, ref reference.Named) (reference.Named, error) { +func FindImageRefMatching(pod v1.PodSpec, selector container.RefSelector) (reference.Named, error) { for _, c := range pod.Containers { cRef, err := container.ParseNamed(c.Image) if err != nil { return nil, errors.Wrap(err, "FindImageRefMatching") } - if cRef.Name() == ref.Name() { + if selector.Matches(cRef) { return cRef, nil } } return nil, nil } -func FindImageNamedTaggedMatching(pod v1.PodSpec, ref reference.Named) (reference.NamedTagged, error) { - cRef, err := FindImageRefMatching(pod, ref) +func FindImageNamedTaggedMatching(pod v1.PodSpec, selector container.RefSelector) (reference.NamedTagged, error) { + cRef, err := FindImageRefMatching(pod, selector) if err != nil { return nil, err } diff --git a/internal/k8s/image_test.go b/internal/k8s/image_test.go index 07054f910c..3dcfd4948d 100644 --- a/internal/k8s/image_test.go +++ b/internal/k8s/image_test.go @@ -9,7 +9,7 @@ import ( "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "github.com/windmilleng/tilt/internal/container" "github.com/windmilleng/tilt/internal/k8s/testyaml" @@ -164,7 +164,7 @@ func TestInjectDigestBlorgBackendYAML(t *testing.T) { entity := entities[1] name := "gcr.io/blorg-dev/blorg-backend" namedTagged, _ := reference.ParseNamed(fmt.Sprintf("%s:wm-tilt", name)) - newEntity, replaced, err := InjectImageDigest(entity, container.NewRefSelector(namedTagged), namedTagged, v1.PullNever) + newEntity, replaced, err := InjectImageDigest(entity, container.NameSelector(namedTagged), namedTagged, v1.PullNever) if err != nil { t.Fatal(err) } @@ -204,7 +204,7 @@ func InjectImageDigestWithStrings(entity K8sEntity, original string, newDigest s return K8sEntity{}, false, err } - return InjectImageDigest(entity, container.NewRefSelector(originalRef), canonicalRef, policy) + return InjectImageDigest(entity, container.NameSelector(originalRef), canonicalRef, policy) } func TestInjectSyncletImage(t *testing.T) { @@ -217,7 +217,7 @@ func TestInjectSyncletImage(t *testing.T) { entity := entities[0] name := "gcr.io/windmill-public-containers/synclet" namedTagged, _ := container.ParseNamedTagged(fmt.Sprintf("%s:tilt-deadbeef", name)) - newEntity, replaced, err := InjectImageDigest(entity, container.NewRefSelector(namedTagged), namedTagged, v1.PullNever) + newEntity, replaced, err := InjectImageDigest(entity, container.NameSelector(namedTagged), namedTagged, v1.PullNever) if err != nil { t.Fatal(err) } else if !replaced { diff --git a/internal/model/manifest.go b/internal/model/manifest.go index 8db032da18..3ac42e5cd6 100644 --- a/internal/model/manifest.go +++ b/internal/model/manifest.go @@ -7,6 +7,7 @@ import ( "k8s.io/apimachinery/pkg/labels" "github.com/docker/distribution/reference" + "github.com/windmilleng/tilt/internal/container" "github.com/windmilleng/tilt/internal/sliceutils" "github.com/google/go-cmp/cmp" @@ -302,6 +303,7 @@ var imageTargetAllowUnexported = cmp.AllowUnexported(ImageTarget{}) var dcTargetAllowUnexported = cmp.AllowUnexported(DockerComposeTarget{}) var labelRequirementAllowUnexported = cmp.AllowUnexported(labels.Requirement{}) var k8sTargetAllowUnexported = cmp.AllowUnexported(K8sTarget{}) +var selectorAllowUnexported = cmp.AllowUnexported(container.RefSelector{}) var dockerRefEqual = cmp.Comparer(func(a, b reference.Named) bool { aNil := a == nil @@ -324,5 +326,6 @@ func DeepEqual(x, y interface{}) bool { dcTargetAllowUnexported, labelRequirementAllowUnexported, k8sTargetAllowUnexported, + selectorAllowUnexported, dockerRefEqual) } diff --git a/internal/synclet/sidecar/inject.go b/internal/synclet/sidecar/inject.go index 8c8eb92d6b..f6c6874f88 100644 --- a/internal/synclet/sidecar/inject.go +++ b/internal/synclet/sidecar/inject.go @@ -1,11 +1,12 @@ package sidecar import ( - "github.com/docker/distribution/reference" + "github.com/windmilleng/tilt/internal/container" "github.com/windmilleng/tilt/internal/k8s" ) -func InjectSyncletSidecar(entity k8s.K8sEntity, matchRef reference.Named) (k8s.K8sEntity, bool, error) { +// Inject the synclet into any Pod +func InjectSyncletSidecar(entity k8s.K8sEntity, selector container.RefSelector) (k8s.K8sEntity, bool, error) { entity = entity.DeepCopy() pods, err := k8s.ExtractPods(&entity) @@ -15,7 +16,7 @@ func InjectSyncletSidecar(entity k8s.K8sEntity, matchRef reference.Named) (k8s.K replaced := false for _, pod := range pods { - ok, err := k8s.PodContainsRef(*pod, matchRef) + ok, err := k8s.PodContainsRef(*pod, selector) if err != nil { return k8s.K8sEntity{}, false, err } diff --git a/internal/synclet/sidecar/sidecar_test.go b/internal/synclet/sidecar/sidecar_test.go index 2ebe188cbf..9ef41a9f04 100644 --- a/internal/synclet/sidecar/sidecar_test.go +++ b/internal/synclet/sidecar/sidecar_test.go @@ -4,8 +4,8 @@ import ( "strings" "testing" - "github.com/docker/distribution/reference" "github.com/stretchr/testify/assert" + "github.com/windmilleng/tilt/internal/container" "github.com/windmilleng/tilt/internal/k8s" "github.com/windmilleng/tilt/internal/k8s/testyaml" ) @@ -18,8 +18,8 @@ func TestInjectSyncletSidecar(t *testing.T) { assert.Equal(t, 1, len(entities)) entity := entities[0] - name, _ := reference.ParseNamed("gcr.io/some-project-162817/sancho") - newEntity, replaced, err := InjectSyncletSidecar(entity, name) + selector := container.MustParseSelector("gcr.io/some-project-162817/sancho") + newEntity, replaced, err := InjectSyncletSidecar(entity, selector) if err != nil { t.Fatal(err) } else if !replaced { diff --git a/internal/tiltfile/build_index.go b/internal/tiltfile/build_index.go index 1887066e9d..e19e3a42f1 100644 --- a/internal/tiltfile/build_index.go +++ b/internal/tiltfile/build_index.go @@ -14,9 +14,9 @@ type buildIndex struct { // keep a slice so that the order of iteration is deterministic images []*dockerImage - taggedImages map[string]*dockerImage - nameOnlyImages map[string]*dockerImage - byTargetID map[model.TargetID]*dockerImage + imagesByName map[string]*dockerImage + imagesBySelector map[string]*dockerImage + byTargetID map[model.TargetID]*dockerImage consumedImageNames []string consumedImageNameMap map[string]bool @@ -24,38 +24,39 @@ type buildIndex struct { func newBuildIndex() *buildIndex { return &buildIndex{ - taggedImages: make(map[string]*dockerImage), - nameOnlyImages: make(map[string]*dockerImage), + imagesBySelector: make(map[string]*dockerImage), + imagesByName: make(map[string]*dockerImage), byTargetID: make(map[model.TargetID]*dockerImage), consumedImageNameMap: make(map[string]bool), } } func (idx *buildIndex) addImage(img *dockerImage) error { - // TODO(nick): Rewrite this index to allow multiple selectors with the - // same name but different tags. - ref := img.ref.AsRef() - refTagged, hasTag := ref.(reference.NamedTagged) - if hasTag { - key := fmt.Sprintf("%s:%s", ref.Name(), refTagged.Tag()) - _, exists := idx.taggedImages[key] - if exists { - return fmt.Errorf("Image for ref %q has already been defined", key) - } + selector := img.ref + name := selector.RefName() + _, hasExisting := idx.imagesBySelector[selector.String()] + if hasExisting { + return fmt.Errorf("Image for ref %q has already been defined", selector.String()) + } - idx.taggedImages[key] = img - } else { - key := ref.Name() - _, exists := idx.nameOnlyImages[key] - if exists { - return fmt.Errorf("Image for ref %q has already been defined", key) - } + idx.imagesBySelector[selector.String()] = img - idx.nameOnlyImages[key] = img + _, hasExistingName := idx.imagesByName[name] + if hasExistingName { + // If the two selectors have the same name but different refs, they must + // have different tags. Make all the selectors "exact", so that they + // only match the exact tag. + img.ref = img.ref.WithExactMatch() + + for _, image := range idx.images { + if image.ref.RefName() == name { + image.ref = image.ref.WithExactMatch() + } + } } + idx.imagesByName[name] = img idx.byTargetID[img.ID()] = img - idx.images = append(idx.images, img) return nil } @@ -76,22 +77,12 @@ func (idx *buildIndex) findBuilderForConsumedImage(ref reference.Named) *dockerI idx.consumedImageNames = append(idx.consumedImageNames, ref.Name()) } - refTagged, hasTag := ref.(reference.NamedTagged) - if hasTag { - key := fmt.Sprintf("%s:%s", ref.Name(), refTagged.Tag()) - img, exists := idx.taggedImages[key] - if exists { - img.matched = true - return img + for _, image := range idx.images { + if image.ref.Matches(ref) { + image.matched = true + return image } } - - key := ref.Name() - img, exists := idx.nameOnlyImages[key] - if exists { - img.matched = true - return img - } return nil } @@ -101,7 +92,7 @@ func (idx *buildIndex) assertAllMatched() error { bagSizes := []int{2, 3, 4} cm := closestmatch.New(idx.consumedImageNames, bagSizes) matchLines := []string{} - for i, match := range cm.ClosestN(image.ref.Name(), 3) { + for i, match := range cm.ClosestN(image.ref.RefName(), 3) { if i == 0 { matchLines = append(matchLines, "Did you mean…") } diff --git a/internal/tiltfile/k8s.go b/internal/tiltfile/k8s.go index 47d0de3630..40cc47b7b1 100644 --- a/internal/tiltfile/k8s.go +++ b/internal/tiltfile/k8s.go @@ -11,6 +11,7 @@ import ( "go.starlark.net/starlark" "k8s.io/apimachinery/pkg/labels" + "github.com/windmilleng/tilt/internal/container" "github.com/windmilleng/tilt/internal/k8s" "github.com/windmilleng/tilt/internal/model" ) @@ -28,16 +29,14 @@ type k8sResource struct { // All k8s resources to be deployed. entities []k8s.K8sEntity - // Image refs that the user manually asked to be associated with this resource. - providedImageRefNames map[string]bool - - // All image refs, including: - // 1) one that the user manually asked to be associated with this resources, and - // 2) images that were auto-inferred from the included k8s resources. - imageRefNames map[string]bool + // Image selectors that the user manually asked to be associated with this resource. + refSelectors []container.RefSelector imageRefs referenceList + // Map of imageRefs, to avoid dupes + imageRefMap map[string]bool + portForwards []portForward // labels for pods that we should watch and associate with this resource @@ -46,13 +45,8 @@ type k8sResource struct { dependencyIDs []model.TargetID } -func (r *k8sResource) addProvidedImageRef(ref reference.Named) { - r.providedImageRefNames[ref.Name()] = true - if !r.imageRefNames[ref.Name()] { - r.imageRefNames[ref.Name()] = true - r.imageRefs = append(r.imageRefs, ref) - sort.Sort(r.imageRefs) - } +func (r *k8sResource) addRefSelector(selector container.RefSelector) { + r.refSelectors = append(r.refSelectors, selector) } func (r *k8sResource) addEntities(entities []k8s.K8sEntity, imageJSONPaths func(e k8s.K8sEntity) []k8s.JSONPath) error { @@ -64,8 +58,8 @@ func (r *k8sResource) addEntities(entities []k8s.K8sEntity, imageJSONPaths func( return err } for _, image := range images { - if !r.imageRefNames[image.Name()] { - r.imageRefNames[image.Name()] = true + if !r.imageRefMap[image.String()] { + r.imageRefMap[image.String()] = true r.imageRefs = append(r.imageRefs, image) } } @@ -74,11 +68,11 @@ func (r *k8sResource) addEntities(entities []k8s.K8sEntity, imageJSONPaths func( return nil } -// Return the provided image refs in a deterministic order. -func (r k8sResource) providedImageRefNameList() []string { - result := make([]string, 0, len(r.providedImageRefNames)) - for ref := range r.providedImageRefNames { - result = append(result, ref) +// Return the image selectors in a deterministic order. +func (r k8sResource) refSelectorList() []string { + result := make([]string, 0, len(r.refSelectors)) + for _, selector := range r.refSelectors { + result = append(result, selector.String()) } sort.Strings(result) return result @@ -249,7 +243,7 @@ func (s *tiltfileState) k8sResource(thread *starlark.Thread, fn *starlark.Builti if err != nil { return nil, err } - r.addProvidedImageRef(imageRef) + r.addRefSelector(container.NewRefSelector(imageRef)) } r.portForwards = portForwards @@ -409,9 +403,8 @@ func (s *tiltfileState) makeK8sResource(name string) (*k8sResource, error) { return nil, fmt.Errorf("k8s_resource named %q already exists", name) } r := &k8sResource{ - name: name, - providedImageRefNames: make(map[string]bool), - imageRefNames: make(map[string]bool), + name: name, + imageRefMap: make(map[string]bool), } s.k8s = append(s.k8s, r) s.k8sByName[name] = r diff --git a/internal/tiltfile/tiltfile_state.go b/internal/tiltfile/tiltfile_state.go index 16cb5bd0ca..7f8300916f 100644 --- a/internal/tiltfile/tiltfile_state.go +++ b/internal/tiltfile/tiltfile_state.go @@ -308,10 +308,10 @@ func (s *tiltfileState) assembleK8sUnresourced() error { func (s *tiltfileState) validateK8s(r *k8sResource) error { if len(r.entities) == 0 { - if len(r.providedImageRefNames) > 0 { + if len(r.refSelectors) > 0 { return fmt.Errorf("resource %q: could not find k8s entities matching "+ "image(s) %q; perhaps there's a typo?", - r.name, strings.Join(r.providedImageRefNameList(), "; ")) + r.name, strings.Join(r.refSelectorList(), "; ")) } return fmt.Errorf("resource %q: could not associate any k8s YAML with this resource", r.name) } @@ -331,15 +331,23 @@ func (s *tiltfileState) validateK8s(r *k8sResource) error { func (s *tiltfileState) k8sResourceForImage(image container.RefSelector) (*k8sResource, error) { // first, look thru all the resources that have already been created, // and see if any of them already have a reference to this image. - refName := image.Name() for _, r := range s.k8s { - if _, ok := r.imageRefNames[refName]; ok { - return r, nil + for _, ref := range r.imageRefs { + if image.Matches(ref) { + return r, nil + } + } + + for _, selector := range r.refSelectors { + if image.RefsEqual(selector) { + return r, nil + } } } // next, look thru all the resources that have already been created, // and see if any of them match the basename of the image. + refName := image.RefName() name := filepath.Base(refName) if r, ok := s.k8sByName[name]; ok { return r, nil @@ -370,9 +378,9 @@ func (s *tiltfileState) findUnresourcedImages() ([]reference.Named, error) { return nil, err } for _, img := range images { - if !seen[img.Name()] { + if !seen[img.String()] { result = append(result, img) - seen[img.Name()] = true + seen[img.String()] = true } } } diff --git a/internal/tiltfile/tiltfile_test.go b/internal/tiltfile/tiltfile_test.go index a18ab4aa71..e84c0336f4 100644 --- a/internal/tiltfile/tiltfile_test.go +++ b/internal/tiltfile/tiltfile_test.go @@ -1540,6 +1540,61 @@ k8s_yaml('auth.yaml') }, f.imageTargetNames(m)) } +func TestImagesWithSameName(t *testing.T) { + f := newFixture(t) + defer f.TearDown() + + f.gitInit("") + f.file("app.dockerfile", "FROM golang:1.10") + f.file("app-jessie.dockerfile", "FROM golang:1.10-jessie") + f.yaml("app.yaml", + deployment("app", image("vandelay/app")), + deployment("app-jessie", image("vandelay/app:jessie"))) + f.file("Tiltfile", ` +docker_build('vandelay/app', '.', dockerfile='app.dockerfile') +docker_build('vandelay/app:jessie', '.', dockerfile='app-jessie.dockerfile') +k8s_yaml('app.yaml') +`) + + f.load() + + m := f.assertNextManifest("app", deployment("app"), deployment("app-jessie")) + assert.Equal(t, []string{ + "docker.io/vandelay/app", + "docker.io/vandelay/app:jessie", + }, f.imageTargetNames(m)) +} + +func TestImagesWithSameNameDifferentManifests(t *testing.T) { + f := newFixture(t) + defer f.TearDown() + + f.gitInit("") + f.file("app.dockerfile", "FROM golang:1.10") + f.file("app-jessie.dockerfile", "FROM golang:1.10-jessie") + f.yaml("app.yaml", + deployment("app", image("vandelay/app")), + deployment("app-jessie", image("vandelay/app:jessie"))) + f.file("Tiltfile", ` +docker_build('vandelay/app', '.', dockerfile='app.dockerfile') +docker_build('vandelay/app:jessie', '.', dockerfile='app-jessie.dockerfile') +k8s_yaml('app.yaml') +k8s_resource('jessie', image='vandelay/app:jessie') +`) + + f.load() + + m := f.assertNextManifest("jessie", deployment("app-jessie")) + assert.Equal(t, []string{ + "docker.io/vandelay/app:jessie", + }, f.imageTargetNames(m)) + + m = f.assertNextManifest("app", deployment("app")) + assert.Equal(t, []string{ + "docker.io/vandelay/app", + }, f.imageTargetNames(m)) +} + func TestImageRefSuggestion(t *testing.T) { f := newFixture(t) defer f.TearDown() @@ -2110,8 +2165,8 @@ func (f *fixture) assertNextManifest(name string, opts ...interface{}) model.Man if ref.Empty() { f.t.Fatalf("manifest %v has no more image refs; expected %q", m.Name, opt.image.ref) } - if ref.Name() != opt.image.ref { - f.t.Fatalf("manifest %v image ref: %q; expected %q", m.Name, ref.Name(), opt.image.ref) + if ref.RefName() != opt.image.ref { + f.t.Fatalf("manifest %v image ref: %q; expected %q", m.Name, ref.RefName(), opt.image.ref) } for _, matcher := range opt.matchers { switch matcher := matcher.(type) { @@ -2128,8 +2183,8 @@ func (f *fixture) assertNextManifest(name string, opts ...interface{}) model.Man case fbHelper: image := nextImageTarget() ref := image.Ref - if ref.Name() != opt.image.ref { - f.t.Fatalf("manifest %v image ref: %q; expected %q", m.Name, ref.Name(), opt.image.ref) + if ref.RefName() != opt.image.ref { + f.t.Fatalf("manifest %v image ref: %q; expected %q", m.Name, ref.RefName(), opt.image.ref) } if !image.IsFastBuild() { @@ -2164,8 +2219,8 @@ func (f *fixture) assertNextManifest(name string, opts ...interface{}) model.Man case cbHelper: image := nextImageTarget() ref := image.Ref - if ref.Name() != opt.image.ref { - f.t.Fatalf("manifest %v image ref: %q; expected %q", m.Name, ref.Name(), opt.image.ref) + if ref.RefName() != opt.image.ref { + f.t.Fatalf("manifest %v image ref: %q; expected %q", m.Name, ref.RefName(), opt.image.ref) } if !image.IsCustomBuild() {