Use the latest resolved value, rather than just the exact value from the
image stream tag.
| ... | ... |
@@ -49,10 +49,10 @@ func (c *ImageChangeController) HandleImageStream(stream *imageapi.ImageStream) |
| 49 | 49 |
} |
| 50 | 50 |
for _, config := range bcs {
|
| 51 | 51 |
var ( |
| 52 |
- from *kapi.ObjectReference |
|
| 53 |
- shouldBuild = false |
|
| 54 |
- triggeredImage = "" |
|
| 55 |
- latest *imageapi.TagEvent |
|
| 52 |
+ from *kapi.ObjectReference |
|
| 53 |
+ shouldBuild = false |
|
| 54 |
+ triggeredImage = "" |
|
| 55 |
+ latestReference string |
|
| 56 | 56 |
) |
| 57 | 57 |
// For every ImageChange trigger find the latest tagged image from the image stream and |
| 58 | 58 |
// invoke a build using that image id. A new build is triggered only if the latest tagged image id or pull spec |
| ... | ... |
@@ -90,8 +90,8 @@ func (c *ImageChangeController) HandleImageStream(stream *imageapi.ImageStream) |
| 90 | 90 |
|
| 91 | 91 |
// This split is safe because ImageStreamTag names always have the form |
| 92 | 92 |
// name:tag. |
| 93 |
- latest = imageapi.LatestTaggedImage(stream, tag) |
|
| 94 |
- if latest == nil {
|
|
| 93 |
+ latestReference, ok = imageapi.ResolveLatestTaggedImage(stream, tag) |
|
| 94 |
+ if !ok {
|
|
| 95 | 95 |
glog.V(4).Infof("unable to find tagged image: no image recorded for %s/%s:%s", stream.Namespace, stream.Name, tag)
|
| 96 | 96 |
continue |
| 97 | 97 |
} |
| ... | ... |
@@ -99,7 +99,7 @@ func (c *ImageChangeController) HandleImageStream(stream *imageapi.ImageStream) |
| 99 | 99 |
|
| 100 | 100 |
// (must be different) to trigger a build |
| 101 | 101 |
last := trigger.ImageChange.LastTriggeredImageID |
| 102 |
- next := latest.DockerImageReference |
|
| 102 |
+ next := latestReference |
|
| 103 | 103 |
|
| 104 | 104 |
if len(last) == 0 || (len(next) > 0 && next != last) {
|
| 105 | 105 |
triggeredImage = next |
| ... | ... |
@@ -123,7 +123,7 @@ func (c *ImageChangeController) HandleImageStream(stream *imageapi.ImageStream) |
| 123 | 123 |
buildapi.BuildTriggerCause{
|
| 124 | 124 |
Message: buildapi.BuildTriggerCauseImageMsg, |
| 125 | 125 |
ImageChangeBuild: &buildapi.ImageChangeCause{
|
| 126 |
- ImageID: latest.DockerImageReference, |
|
| 126 |
+ ImageID: latestReference, |
|
| 127 | 127 |
FromRef: from, |
| 128 | 128 |
}, |
| 129 | 129 |
}, |
| ... | ... |
@@ -56,8 +56,8 @@ func (g *DeploymentConfigGenerator) Generate(ctx kapi.Context, name string) (*de |
| 56 | 56 |
} |
| 57 | 57 |
|
| 58 | 58 |
// Find the latest tag event for the trigger tag |
| 59 |
- latestEvent := imageapi.LatestTaggedImage(imageStream, tag) |
|
| 60 |
- if latestEvent == nil {
|
|
| 59 |
+ latestReference, ok := imageapi.ResolveLatestTaggedImage(imageStream, tag) |
|
| 60 |
+ if !ok {
|
|
| 61 | 61 |
f := field.NewPath("triggers").Index(i).Child("imageChange", "tag")
|
| 62 | 62 |
errs = append(errs, field.Invalid(f, tag, fmt.Sprintf("no image recorded for %s/%s:%s", imageStream.Namespace, imageStream.Name, tag)))
|
| 63 | 63 |
continue |
| ... | ... |
@@ -72,12 +72,12 @@ func (g *DeploymentConfigGenerator) Generate(ctx kapi.Context, name string) (*de |
| 72 | 72 |
if !names.Has(container.Name) {
|
| 73 | 73 |
continue |
| 74 | 74 |
} |
| 75 |
- if len(latestEvent.DockerImageReference) > 0 && |
|
| 76 |
- container.Image != latestEvent.DockerImageReference {
|
|
| 75 |
+ if len(latestReference) > 0 && |
|
| 76 |
+ container.Image != latestReference {
|
|
| 77 | 77 |
// Update the image |
| 78 |
- container.Image = latestEvent.DockerImageReference |
|
| 78 |
+ container.Image = latestReference |
|
| 79 | 79 |
// Log the last triggered image ID |
| 80 |
- params.LastTriggeredImage = latestEvent.DockerImageReference |
|
| 80 |
+ params.LastTriggeredImage = latestReference |
|
| 81 | 81 |
containerChanged = true |
| 82 | 82 |
} |
| 83 | 83 |
} |
| ... | ... |
@@ -136,14 +136,13 @@ func processTriggers(config *deployapi.DeploymentConfig, isn client.ImageStreams |
| 136 | 136 |
} |
| 137 | 137 |
|
| 138 | 138 |
// Find the latest tag event for the trigger reference. |
| 139 |
- latestEvent := imageapi.LatestTaggedImage(stream, tag) |
|
| 140 |
- if latestEvent == nil {
|
|
| 139 |
+ latestReference, ok := imageapi.ResolveLatestTaggedImage(stream, tag) |
|
| 140 |
+ if !ok {
|
|
| 141 | 141 |
continue |
| 142 | 142 |
} |
| 143 | 143 |
|
| 144 | 144 |
// Ensure a change occurred |
| 145 |
- latestRef := latestEvent.DockerImageReference |
|
| 146 |
- if len(latestRef) == 0 || latestRef == params.LastTriggeredImage {
|
|
| 145 |
+ if len(latestReference) == 0 || latestReference == params.LastTriggeredImage {
|
|
| 147 | 146 |
continue |
| 148 | 147 |
} |
| 149 | 148 |
|
| ... | ... |
@@ -155,11 +154,11 @@ func processTriggers(config *deployapi.DeploymentConfig, isn client.ImageStreams |
| 155 | 155 |
continue |
| 156 | 156 |
} |
| 157 | 157 |
|
| 158 |
- if container.Image != latestRef {
|
|
| 158 |
+ if container.Image != latestReference {
|
|
| 159 | 159 |
// Update the image |
| 160 |
- container.Image = latestRef |
|
| 160 |
+ container.Image = latestReference |
|
| 161 | 161 |
// Log the last triggered image ID |
| 162 |
- params.LastTriggeredImage = latestRef |
|
| 162 |
+ params.LastTriggeredImage = latestReference |
|
| 163 | 163 |
} |
| 164 | 164 |
} |
| 165 | 165 |
} |
| ... | ... |
@@ -565,6 +565,55 @@ func LatestTaggedImage(stream *ImageStream, tag string) *TagEvent {
|
| 565 | 565 |
return nil |
| 566 | 566 |
} |
| 567 | 567 |
|
| 568 |
+// ResolveLatestTaggedImage returns the appropriate pull spec for a given tag in |
|
| 569 |
+// the image stream, handling the tag's reference policy if necessary to return |
|
| 570 |
+// a resolved image. Callers that transform an ImageStreamTag into a pull spec |
|
| 571 |
+// should use this method instead of LatestTaggedImage. |
|
| 572 |
+func ResolveLatestTaggedImage(stream *ImageStream, tag string) (string, bool) {
|
|
| 573 |
+ if len(tag) == 0 {
|
|
| 574 |
+ tag = DefaultImageTag |
|
| 575 |
+ } |
|
| 576 |
+ |
|
| 577 |
+ // retrieve event |
|
| 578 |
+ latest := LatestTaggedImage(stream, tag) |
|
| 579 |
+ if latest == nil {
|
|
| 580 |
+ return "", false |
|
| 581 |
+ } |
|
| 582 |
+ |
|
| 583 |
+ // retrieve spec policy - if not found, we use the latest spec |
|
| 584 |
+ ref, ok := stream.Spec.Tags[tag] |
|
| 585 |
+ if !ok {
|
|
| 586 |
+ return latest.DockerImageReference, true |
|
| 587 |
+ } |
|
| 588 |
+ |
|
| 589 |
+ switch ref.ReferencePolicy.Type {
|
|
| 590 |
+ // the local reference policy attempts to use image pull through on the integrated |
|
| 591 |
+ // registry if possible |
|
| 592 |
+ case LocalTagReferencePolicy: |
|
| 593 |
+ local := stream.Status.DockerImageRepository |
|
| 594 |
+ if len(local) == 0 || len(latest.Image) == 0 {
|
|
| 595 |
+ // fallback to the originating reference if no local docker registry defined or we |
|
| 596 |
+ // lack an image ID |
|
| 597 |
+ return latest.DockerImageReference, true |
|
| 598 |
+ } |
|
| 599 |
+ |
|
| 600 |
+ ref, err := ParseDockerImageReference(local) |
|
| 601 |
+ if err != nil {
|
|
| 602 |
+ // fallback to the originating reference if the reported local repository spec is not valid |
|
| 603 |
+ return latest.DockerImageReference, true |
|
| 604 |
+ } |
|
| 605 |
+ |
|
| 606 |
+ // create a local pullthrough URL |
|
| 607 |
+ ref.Tag = "" |
|
| 608 |
+ ref.ID = latest.Image |
|
| 609 |
+ return ref.Exact(), true |
|
| 610 |
+ |
|
| 611 |
+ // the default policy is to use the originating image |
|
| 612 |
+ default: |
|
| 613 |
+ return latest.DockerImageReference, true |
|
| 614 |
+ } |
|
| 615 |
+} |
|
| 616 |
+ |
|
| 568 | 617 |
// DifferentTagEvent returns true if the supplied tag event matches the current stream tag event. |
| 569 | 618 |
// Generation is not compared. |
| 570 | 619 |
func DifferentTagEvent(stream *ImageStream, tag string, next TagEvent) bool {
|
| ... | ... |
@@ -839,6 +839,158 @@ func TestLatestTaggedImage(t *testing.T) {
|
| 839 | 839 |
} |
| 840 | 840 |
} |
| 841 | 841 |
|
| 842 |
+func TestResolveLatestTaggedImage(t *testing.T) {
|
|
| 843 |
+ tests := []struct {
|
|
| 844 |
+ tag string |
|
| 845 |
+ statusRef string |
|
| 846 |
+ refs map[string]TagReference |
|
| 847 |
+ tags map[string]TagEventList |
|
| 848 |
+ expected string |
|
| 849 |
+ expectNotFound bool |
|
| 850 |
+ }{
|
|
| 851 |
+ {
|
|
| 852 |
+ tag: "foo", |
|
| 853 |
+ tags: map[string]TagEventList{},
|
|
| 854 |
+ expectNotFound: true, |
|
| 855 |
+ }, |
|
| 856 |
+ {
|
|
| 857 |
+ tag: "foo", |
|
| 858 |
+ tags: map[string]TagEventList{
|
|
| 859 |
+ "latest": {
|
|
| 860 |
+ Items: []TagEvent{
|
|
| 861 |
+ {DockerImageReference: "latest-ref"},
|
|
| 862 |
+ {DockerImageReference: "older"},
|
|
| 863 |
+ }, |
|
| 864 |
+ }, |
|
| 865 |
+ }, |
|
| 866 |
+ expectNotFound: true, |
|
| 867 |
+ }, |
|
| 868 |
+ {
|
|
| 869 |
+ tag: "", |
|
| 870 |
+ tags: map[string]TagEventList{
|
|
| 871 |
+ "latest": {
|
|
| 872 |
+ Items: []TagEvent{
|
|
| 873 |
+ {DockerImageReference: "latest-ref"},
|
|
| 874 |
+ {DockerImageReference: "older"},
|
|
| 875 |
+ }, |
|
| 876 |
+ }, |
|
| 877 |
+ }, |
|
| 878 |
+ expected: "latest-ref", |
|
| 879 |
+ }, |
|
| 880 |
+ {
|
|
| 881 |
+ tag: "foo", |
|
| 882 |
+ tags: map[string]TagEventList{
|
|
| 883 |
+ "latest": {
|
|
| 884 |
+ Items: []TagEvent{
|
|
| 885 |
+ {DockerImageReference: "latest-ref"},
|
|
| 886 |
+ {DockerImageReference: "older"},
|
|
| 887 |
+ }, |
|
| 888 |
+ }, |
|
| 889 |
+ "foo": {
|
|
| 890 |
+ Items: []TagEvent{
|
|
| 891 |
+ {DockerImageReference: "foo-ref"},
|
|
| 892 |
+ {DockerImageReference: "older"},
|
|
| 893 |
+ }, |
|
| 894 |
+ }, |
|
| 895 |
+ }, |
|
| 896 |
+ expected: "foo-ref", |
|
| 897 |
+ }, |
|
| 898 |
+ |
|
| 899 |
+ // the default reference policy does nothing |
|
| 900 |
+ {
|
|
| 901 |
+ refs: map[string]TagReference{
|
|
| 902 |
+ "latest": {
|
|
| 903 |
+ ReferencePolicy: TagReferencePolicy{Type: SourceTagReferencePolicy},
|
|
| 904 |
+ }, |
|
| 905 |
+ }, |
|
| 906 |
+ tags: map[string]TagEventList{
|
|
| 907 |
+ "latest": {
|
|
| 908 |
+ Items: []TagEvent{
|
|
| 909 |
+ {DockerImageReference: "latest-ref", Image: "sha256:4ab15c48b859c2920dd5224f92aabcd39a52794c5b3cf088fb3bbb438756c246"},
|
|
| 910 |
+ {DockerImageReference: "older"},
|
|
| 911 |
+ }, |
|
| 912 |
+ }, |
|
| 913 |
+ }, |
|
| 914 |
+ expected: "latest-ref", |
|
| 915 |
+ }, |
|
| 916 |
+ |
|
| 917 |
+ // the local reference policy does nothing unless reference is set |
|
| 918 |
+ {
|
|
| 919 |
+ refs: map[string]TagReference{
|
|
| 920 |
+ "latest": {
|
|
| 921 |
+ ReferencePolicy: TagReferencePolicy{Type: LocalTagReferencePolicy},
|
|
| 922 |
+ }, |
|
| 923 |
+ }, |
|
| 924 |
+ tags: map[string]TagEventList{
|
|
| 925 |
+ "latest": {
|
|
| 926 |
+ Items: []TagEvent{
|
|
| 927 |
+ {DockerImageReference: "latest-ref", Image: "sha256:4ab15c48b859c2920dd5224f92aabcd39a52794c5b3cf088fb3bbb438756c246"},
|
|
| 928 |
+ {DockerImageReference: "older"},
|
|
| 929 |
+ }, |
|
| 930 |
+ }, |
|
| 931 |
+ }, |
|
| 932 |
+ expected: "latest-ref", |
|
| 933 |
+ }, |
|
| 934 |
+ |
|
| 935 |
+ // the local reference policy does nothing unless the image id is set |
|
| 936 |
+ {
|
|
| 937 |
+ statusRef: "test.server/a/b", |
|
| 938 |
+ refs: map[string]TagReference{
|
|
| 939 |
+ "latest": {
|
|
| 940 |
+ ReferencePolicy: TagReferencePolicy{Type: LocalTagReferencePolicy},
|
|
| 941 |
+ }, |
|
| 942 |
+ }, |
|
| 943 |
+ tags: map[string]TagEventList{
|
|
| 944 |
+ "latest": {
|
|
| 945 |
+ Items: []TagEvent{
|
|
| 946 |
+ {DockerImageReference: "latest-ref"},
|
|
| 947 |
+ {DockerImageReference: "older"},
|
|
| 948 |
+ }, |
|
| 949 |
+ }, |
|
| 950 |
+ }, |
|
| 951 |
+ expected: "latest-ref", |
|
| 952 |
+ }, |
|
| 953 |
+ |
|
| 954 |
+ // the local reference policy uses the output status reference and the image id |
|
| 955 |
+ // and returns a pullthrough spec |
|
| 956 |
+ {
|
|
| 957 |
+ statusRef: "test.server/a/b", |
|
| 958 |
+ refs: map[string]TagReference{
|
|
| 959 |
+ "latest": {
|
|
| 960 |
+ ReferencePolicy: TagReferencePolicy{Type: LocalTagReferencePolicy},
|
|
| 961 |
+ }, |
|
| 962 |
+ }, |
|
| 963 |
+ tags: map[string]TagEventList{
|
|
| 964 |
+ "latest": {
|
|
| 965 |
+ Items: []TagEvent{
|
|
| 966 |
+ {DockerImageReference: "latest-ref", Image: "sha256:4ab15c48b859c2920dd5224f92aabcd39a52794c5b3cf088fb3bbb438756c246"},
|
|
| 967 |
+ {DockerImageReference: "older"},
|
|
| 968 |
+ }, |
|
| 969 |
+ }, |
|
| 970 |
+ }, |
|
| 971 |
+ expected: "test.server/a/b@sha256:4ab15c48b859c2920dd5224f92aabcd39a52794c5b3cf088fb3bbb438756c246", |
|
| 972 |
+ }, |
|
| 973 |
+ } |
|
| 974 |
+ |
|
| 975 |
+ for i, test := range tests {
|
|
| 976 |
+ stream := &ImageStream{}
|
|
| 977 |
+ stream.Status.DockerImageRepository = test.statusRef |
|
| 978 |
+ stream.Status.Tags = test.tags |
|
| 979 |
+ stream.Spec.Tags = test.refs |
|
| 980 |
+ |
|
| 981 |
+ actual, ok := ResolveLatestTaggedImage(stream, test.tag) |
|
| 982 |
+ if !ok {
|
|
| 983 |
+ if !test.expectNotFound {
|
|
| 984 |
+ t.Errorf("%d: unexpected nil result", i)
|
|
| 985 |
+ } |
|
| 986 |
+ continue |
|
| 987 |
+ } |
|
| 988 |
+ if e, a := test.expected, actual; e != a {
|
|
| 989 |
+ t.Errorf("%d: expected %q, got %q", i, e, a)
|
|
| 990 |
+ } |
|
| 991 |
+ } |
|
| 992 |
+} |
|
| 993 |
+ |
|
| 842 | 994 |
func TestAddTagEventToImageStream(t *testing.T) {
|
| 843 | 995 |
tests := map[string]struct {
|
| 844 | 996 |
tags map[string]TagEventList |