Skip to content

Commit afa6a25

Browse files
Merge pull request #18513 from tnozicka/fix-annotation-trigger-reconciliation
Automatic merge from submit-queue (batch tested with PRs 18503, 18399, 13701, 18513, 18515). Fix annotation trigger to reconcile on container image change Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1541685 @smarterclayton this should fix your issues with DS and annotation trigger
2 parents 0fd7db9 + 7057684 commit afa6a25

File tree

7 files changed

+221
-0
lines changed

7 files changed

+221
-0
lines changed

pkg/image/OWNERS

+2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ reviewers:
55
- miminar
66
- mfojtik
77
- liggitt
8+
- tnozicka
89
approvers:
910
- bparees
1011
- smarterclayton
1112
- soltysh
1213
- mfojtik
1314
- liggitt
15+
- tnozicka

pkg/image/trigger/annotations/annotations.go

+29
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,33 @@ func UpdateObjectFromImages(obj runtime.Object, tagRetriever trigger.TagRetrieve
172172
return updated, nil
173173
}
174174

175+
// ContainerImageChanged returns true if any container image referenced by newTriggers changed.
176+
func ContainerImageChanged(oldObj, newObj runtime.Object, newTriggers []triggerapi.ObjectFieldTrigger) bool {
177+
for _, trigger := range newTriggers {
178+
if trigger.Paused {
179+
continue
180+
}
181+
182+
newContainer, _, err := ContainerForObjectFieldPath(newObj, trigger.FieldPath)
183+
if err != nil {
184+
glog.V(5).Infof("%v", err)
185+
continue
186+
}
187+
188+
oldContainer, _, err := ContainerForObjectFieldPath(oldObj, trigger.FieldPath)
189+
if err != nil {
190+
// might just be a result of the update
191+
continue
192+
}
193+
194+
if newContainer.GetImage() != oldContainer.GetImage() {
195+
return true
196+
}
197+
}
198+
199+
return false
200+
}
201+
175202
// annotationTriggerIndexer uses annotations on objects to trigger changes.
176203
type annotationTriggerIndexer struct {
177204
prefix string
@@ -236,6 +263,8 @@ func (i annotationTriggerIndexer) Index(obj, old interface{}) (string, *trigger.
236263
change = cache.Added
237264
case !reflect.DeepEqual(oldTriggers, triggers):
238265
change = cache.Updated
266+
case ContainerImageChanged(old.(runtime.Object), obj.(runtime.Object), triggers):
267+
change = cache.Updated
239268
}
240269
}
241270

test/extended/extended_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
_ "github.com/openshift/origin/test/extended/image_ecosystem"
1515
_ "github.com/openshift/origin/test/extended/imageapis"
1616
_ "github.com/openshift/origin/test/extended/images"
17+
_ "github.com/openshift/origin/test/extended/images/trigger"
1718
_ "github.com/openshift/origin/test/extended/jobs"
1819
_ "github.com/openshift/origin/test/extended/localquota"
1920
_ "github.com/openshift/origin/test/extended/networking"
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package trigger
2+
3+
import (
4+
"time"
5+
6+
g "github.com/onsi/ginkgo"
7+
o "github.com/onsi/gomega"
8+
9+
appsv1 "k8s.io/api/apps/v1"
10+
"k8s.io/apimachinery/pkg/types"
11+
"k8s.io/kubernetes/test/e2e/framework"
12+
13+
exutil "github.com/openshift/origin/test/extended/util"
14+
)
15+
16+
var (
17+
SyncTimeout = 30 * time.Second
18+
)
19+
20+
var _ = g.Describe("[Feature:AnnotationTrigger] Annotation trigger", func() {
21+
defer g.GinkgoRecover()
22+
23+
oc := exutil.NewCLI("cli-deployment", exutil.KubeConfigPath())
24+
25+
var (
26+
deploymentFixture = exutil.FixturePath("testdata", "image", "deployment-with-annotation-trigger.yaml")
27+
)
28+
29+
g.It("reconciles after the image is overwritten", func() {
30+
namespace := oc.Namespace()
31+
32+
g.By("creating a Deployment")
33+
deployment, err := readDeploymentFixture(deploymentFixture)
34+
o.Expect(err).NotTo(o.HaveOccurred())
35+
o.Expect(deployment.Spec.Template.Spec.Containers).To(o.HaveLen(1))
36+
o.Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(o.Equal(" "))
37+
38+
deployment, err = oc.KubeClient().AppsV1().Deployments(namespace).Create(deployment)
39+
o.Expect(err).NotTo(o.HaveOccurred())
40+
o.Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(o.Equal(" "))
41+
42+
g.By("tagging the docker.io/library/centos:latest as test:v1 image to create ImageStream")
43+
out, err := oc.Run("tag").Args("docker.io/library/centos:latest", "test:v1").Output()
44+
framework.Logf("%s", out)
45+
o.Expect(err).NotTo(o.HaveOccurred())
46+
47+
g.By("waiting for the initial image to be replaced from ImageStream")
48+
deployment, err = waitForDeploymentModification(oc.KubeClient().AppsV1(), deployment.ObjectMeta, SyncTimeout, func(d *appsv1.Deployment) (bool, error) {
49+
return d.Spec.Template.Spec.Containers[0].Image != deployment.Spec.Template.Spec.Containers[0].Image, nil
50+
})
51+
o.Expect(err).NotTo(o.HaveOccurred())
52+
53+
g.By("setting Deployment image repeatedly to ' ' to fight with annotation trigger")
54+
for i := 0; i < 50; i++ {
55+
deployment, err = oc.KubeClient().AppsV1().Deployments(namespace).Patch(deployment.Name, types.StrategicMergePatchType,
56+
[]byte(`{"spec":{"template":{"spec":{"containers":[{"name":"test","image":" "}]}}}}`))
57+
o.Expect(err).NotTo(o.HaveOccurred())
58+
o.Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(o.Equal(" "))
59+
}
60+
61+
g.By("waiting for the image to be injected by annotation trigger")
62+
o.Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(o.Equal(" "))
63+
deployment, err = waitForDeploymentModification(oc.KubeClient().AppsV1(), deployment.ObjectMeta, SyncTimeout, func(d *appsv1.Deployment) (bool, error) {
64+
return d.Spec.Template.Spec.Containers[0].Image != deployment.Spec.Template.Spec.Containers[0].Image, nil
65+
})
66+
o.Expect(err).NotTo(o.HaveOccurred())
67+
})
68+
})

test/extended/images/trigger/utils.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package trigger
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"time"
7+
8+
"github.com/ghodss/yaml"
9+
10+
appsv1 "k8s.io/api/apps/v1"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/apimachinery/pkg/watch"
13+
appsv1clientset "k8s.io/client-go/kubernetes/typed/apps/v1"
14+
)
15+
16+
func readDeploymentFixture(path string) (*appsv1.Deployment, error) {
17+
data, err := ioutil.ReadFile(path)
18+
if err != nil {
19+
return nil, err
20+
}
21+
22+
deployment := new(appsv1.Deployment)
23+
err = yaml.Unmarshal(data, deployment)
24+
if err != nil {
25+
return nil, err
26+
}
27+
28+
return deployment, err
29+
}
30+
31+
func waitForDeploymentModification(appsClient appsv1clientset.AppsV1Interface, objMeta metav1.ObjectMeta, timeout time.Duration, condition func(deployment *appsv1.Deployment) (bool, error)) (*appsv1.Deployment, error) {
32+
watcher, err := appsClient.Deployments(objMeta.Namespace).Watch(metav1.SingleObject(objMeta))
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
event, err := watch.Until(timeout, watcher, func(event watch.Event) (bool, error) {
38+
if event.Type != watch.Modified && (objMeta.ResourceVersion == "" && event.Type != watch.Added) {
39+
return true, fmt.Errorf("different kind of event appeared while waiting for Deployment modification: event: %#v", event)
40+
}
41+
return condition(event.Object.(*appsv1.Deployment))
42+
})
43+
if err != nil {
44+
return nil, err
45+
}
46+
return event.Object.(*appsv1.Deployment), nil
47+
}

test/extended/testdata/bindata.go

+48
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
annotations:
5+
image.openshift.io/triggers: '[{"from":{"kind":"ImageStreamTag","name":"test:v1"},"fieldPath":"spec.template.spec.containers[?(@.name==\"test\")].image"}]'
6+
name: test
7+
spec:
8+
progressDeadlineSeconds: 600
9+
replicas: 1
10+
revisionHistoryLimit: 10
11+
selector:
12+
matchLabels:
13+
app: test
14+
strategy:
15+
type: Recreate
16+
template:
17+
metadata:
18+
labels:
19+
app: test
20+
spec:
21+
containers:
22+
- image: " "
23+
name: test
24+
command: ["/bin/sleep"]
25+
args:
26+
- infinity

0 commit comments

Comments
 (0)