Skip to content

build: take referencePolicy into account when resolving istag #12767

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions pkg/build/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -575,25 +575,48 @@ func (g *BuildGenerator) resolveImageStreamReference(ctx kapi.Context, from kapi
glog.V(4).Infof("Resolving ImageStreamReference %s of Kind %s in namespace %s", from.Name, from.Kind, namespace)
switch from.Kind {
case "ImageStreamImage":
imageStreamImage, err := g.Client.GetImageStreamImage(kapi.WithNamespace(ctx, namespace), from.Name)
name, id, err := imageapi.ParseImageStreamImageName(from.Name)
if err != nil {
err = resolveError(from.Kind, namespace, from.Name, err)
glog.V(2).Info(err)
return "", err
}
image := imageStreamImage.Image
glog.V(4).Infof("Resolved ImageStreamReference %s to image %s with reference %s in namespace %s", from.Name, image.Name, image.DockerImageReference, namespace)
return image.DockerImageReference, nil
stream, err := g.Client.GetImageStream(kapi.WithNamespace(ctx, namespace), name)
if err != nil {
err = resolveError(from.Kind, namespace, from.Name, err)
glog.V(2).Info(err)
return "", err
}
reference, ok := imageapi.DockerImageReferenceForImage(stream, id)
if !ok {
err = resolveError(from.Kind, namespace, from.Name, fmt.Errorf("unable to find corresponding tag for image %q", id))
glog.V(2).Info(err)
return "", err
}
glog.V(4).Infof("Resolved ImageStreamImage %s to image %q", from.Name, reference)
return reference, nil

case "ImageStreamTag":
imageStreamTag, err := g.Client.GetImageStreamTag(kapi.WithNamespace(ctx, namespace), from.Name)
name, tag, err := imageapi.ParseImageStreamTagName(from.Name)
if err != nil {
err = resolveError(from.Kind, namespace, from.Name, err)
glog.V(2).Info(err)
return "", err
}
image := imageStreamTag.Image
glog.V(4).Infof("Resolved ImageStreamTag %s to image %s with reference %s in namespace %s", from.Name, image.Name, image.DockerImageReference, namespace)
return image.DockerImageReference, nil
stream, err := g.Client.GetImageStream(kapi.WithNamespace(ctx, namespace), name)
if err != nil {
err = resolveError(from.Kind, namespace, from.Name, err)
glog.V(2).Info(err)
return "", err
}
reference, ok := imageapi.ResolveLatestTaggedImage(stream, tag)
if !ok {
err = resolveError(from.Kind, namespace, from.Name, fmt.Errorf("unable to find latest tagged image"))
glog.V(2).Info(err)
return "", err
}
glog.V(4).Infof("Resolved ImageStreamTag %s to image %q", from.Name, reference)
return reference, nil
case "DockerImage":
return from.Name, nil
default:
Expand Down Expand Up @@ -672,7 +695,7 @@ func (g *BuildGenerator) resolveImageSecret(ctx kapi.Context, secrets []kapi.Sec

func resolveError(kind string, namespace string, name string, err error) error {
msg := fmt.Sprintf("Error resolving %s %s in namespace %s: %v", kind, name, namespace, err)
return &errors.StatusError{unversioned.Status{
return &errors.StatusError{ErrStatus: unversioned.Status{
Status: unversioned.StatusFailure,
Code: errors.StatusUnprocessableEntity,
Reason: unversioned.StatusReasonInvalid,
Expand Down
51 changes: 41 additions & 10 deletions pkg/build/generator/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,38 @@ func TestInstantiateWithImageTrigger(t *testing.T) {
bc = buildConfig
return nil
}
client.GetImageStreamFunc =
func(ctx kapi.Context, name string) (*imageapi.ImageStream, error) {
return &imageapi.ImageStream{
ObjectMeta: kapi.ObjectMeta{Name: name},
Status: imageapi.ImageStreamStatus{
DockerImageRepository: originalImage,
Tags: map[string]imageapi.TagEventList{
"tag1": {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bparees this looks ugly but I haven't found a better way to inject the tag the trigger expects to change :-)

Items: []imageapi.TagEvent{
{
DockerImageReference: "ref/" + name + ":tag1",
},
},
},
"tag2": {
Items: []imageapi.TagEvent{
{
DockerImageReference: "ref/" + name + ":tag2",
},
},
},
"tag3": {
Items: []imageapi.TagEvent{
{
DockerImageReference: "ref/" + name + ":tag3",
},
},
},
},
},
}, nil
}
generator.Client = client

req := &buildapi.BuildRequest{
Expand All @@ -395,7 +427,7 @@ func TestInstantiateWithImageTrigger(t *testing.T) {
if i == tc.triggerIndex {
// Verify that the trigger got updated
if bc.Spec.Triggers[i].ImageChange.LastTriggeredImageID != imageID {
t.Errorf("%s: expeccted trigger at index %d to contain imageID %s", tc.name, i, imageID)
t.Errorf("%s: expected trigger at index %d to contain imageID %s", tc.name, i, imageID)
}
continue
}
Expand All @@ -405,8 +437,8 @@ func TestInstantiateWithImageTrigger(t *testing.T) {
if from == nil {
from = buildutil.GetInputReference(bc.Spec.Strategy)
}
if bc.Spec.Triggers[i].ImageChange.LastTriggeredImageID != ("ref@" + from.Name) {
t.Errorf("%s: expected LastTriggeredImageID for trigger at %d to be %s. Got: %s", tc.name, i, "ref@"+from.Name, bc.Spec.Triggers[i].ImageChange.LastTriggeredImageID)
if bc.Spec.Triggers[i].ImageChange.LastTriggeredImageID != ("ref/" + from.Name) {
t.Errorf("%s: expected LastTriggeredImageID for trigger at %d (%+v) to be %s. Got: %s", tc.name, i, bc.Spec.Triggers[i].ImageChange.From, "ref/"+from.Name, bc.Spec.Triggers[i].ImageChange.LastTriggeredImageID)
}
}
}
Expand Down Expand Up @@ -447,16 +479,16 @@ func TestInstantiateWithLastVersion(t *testing.T) {
func TestInstantiateWithMissingImageStream(t *testing.T) {
g := mockBuildGenerator()
c := g.Client.(Client)
c.GetImageStreamTagFunc = func(ctx kapi.Context, name string) (*imageapi.ImageStreamTag, error) {
return nil, errors.NewNotFound(imageapi.Resource("imagestreamtags"), "testRepo")
c.GetImageStreamFunc = func(ctx kapi.Context, name string) (*imageapi.ImageStream, error) {
return nil, errors.NewNotFound(imageapi.Resource("imagestreams"), "testRepo")
}
g.Client = c

_, err := g.Instantiate(kapi.NewDefaultContext(), &buildapi.BuildRequest{})
se, ok := err.(*errors.StatusError)

if !ok {
t.Errorf("Expected errors.StatusError, got %T", err)
t.Fatalf("Expected errors.StatusError, got %T", err)
}

if se.ErrStatus.Code != errors.StatusUnprocessableEntity {
Expand Down Expand Up @@ -1421,7 +1453,7 @@ func TestResolveImageStreamRef(t *testing.T) {
Name: imageRepoName + ":" + tagName,
},
expectedSuccess: true,
expectedDockerRef: latestDockerReference,
expectedDockerRef: dockerReference,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bparees this one is weird... you are trying to resolve the "foo:test" to "foo:latestDockerReference", can you or somebody explain the intention of this test?

Copy link
Contributor

@bparees bparees Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're trying to resolve the image stream tag "testRepo:test" to the docker image reference "latestDockerReference"

the intent being to prove we properly convert imagestreamtags (which builds can't consume) to dockerimagereferences (which they can)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you've switched from using the mocked GetImageStreamTagFunc to the GetImageStreamFunc, you're getting different behavior. The GetImageStreamTag func always returned a DockerImageRef of "latestDockerReference" but the GetImageStreamFunc returns two tags, one for dockerReference and one for latestDockerReference. So your change is probably fine.

},
{
streamRef: kapi.ObjectReference{
Expand All @@ -1445,7 +1477,7 @@ func TestResolveImageStreamRef(t *testing.T) {
t.Errorf("Scenario %d: did not get expected error", i)
}
if ref != test.expectedDockerRef {
t.Errorf("Scenario %d: Resolved reference %s did not match expected value %s", i, ref, test.expectedDockerRef)
t.Errorf("Scenario %d: Resolved reference %q did not match expected value %q", i, ref, test.expectedDockerRef)
}
}
}
Expand Down Expand Up @@ -1554,7 +1586,6 @@ func mockBuildGeneratorForInstantiate() *BuildGenerator {
},
}, nil
}
g.Client = c
return g
}

Expand Down Expand Up @@ -1603,7 +1634,7 @@ func mockBuildGenerator() *BuildGenerator {
},
imageapi.DefaultImageTag: {
Items: []imageapi.TagEvent{
{DockerImageReference: latestDockerReference},
{DockerImageReference: latestDockerReference, Image: "myid"},
},
},
},
Expand Down
52 changes: 52 additions & 0 deletions pkg/image/api/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,30 @@ func FollowTagReference(stream *ImageStream, tag string) (finalTag string, ref *
}
}

// LatestImageTagEvent returns the most recent TagEvent and the tag for the specified
// image.
func LatestImageTagEvent(stream *ImageStream, imageID string) (string, *TagEvent) {
var (
latestTagEvent *TagEvent
latestTag string
)
for tag, events := range stream.Status.Tags {
if len(events.Items) == 0 {
continue
}
for i, event := range events.Items {
if event.Image != imageID {
continue
}
if latestTagEvent == nil || (latestTagEvent != nil && event.Created.After(latestTagEvent.Created.Time)) {
latestTagEvent = &events.Items[i]
latestTag = tag
}
}
}
return latestTag, latestTagEvent
}

// LatestTaggedImage returns the most recent TagEvent for the specified image
// repository and tag. Will resolve lookups for the empty tag. Returns nil
// if tag isn't present in stream.status.tags.
Expand Down Expand Up @@ -610,6 +634,34 @@ func ResolveLatestTaggedImage(stream *ImageStream, tag string) (string, bool) {
}
}

// DockerImageReferenceForImage returns the docker reference for specified image. Assuming
// the image stream contains the image and the image has corresponding tag, this function
// will try to find this tag and take the reference policy into the account.
// If the image stream does not reference the image or the image does not have
// corresponding tag event, this function will return false.
func DockerImageReferenceForImage(stream *ImageStream, imageID string) (string, bool) {
tag, event := LatestImageTagEvent(stream, imageID)
if len(tag) == 0 {
return "", false
}
ref, ok := stream.Spec.Tags[tag]
if !ok {
return event.DockerImageReference, true
}
switch ref.ReferencePolicy.Type {
case LocalTagReferencePolicy:
ref, err := ParseDockerImageReference(stream.Status.DockerImageRepository)
if err != nil {
return event.DockerImageReference, true
}
ref.Tag = ""
ref.ID = event.Image
return ref.Exact(), true
default:
return event.DockerImageReference, true
}
}

// DifferentTagEvent returns true if the supplied tag event matches the current stream tag event.
// Generation is not compared.
func DifferentTagEvent(stream *ImageStream, tag string, next TagEvent) bool {
Expand Down