Skip to content

Commit 8ed678c

Browse files
Merge pull request #359 from buildpacks-community/issue-278-import-cmd-retry
[Issue-278] Waiter uses watchTools.UntilWithSync instead of Until to handle resource version old error
2 parents 3a0b1d8 + 3ed0c4b commit 8ed678c

3 files changed

Lines changed: 82 additions & 30 deletions

File tree

pkg/commands/import/import.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package _import
55

66
import (
77
"io"
8-
"io/ioutil"
98
"os"
109

1110
"github.com/spf13/cobra"
@@ -170,7 +169,7 @@ func readDescriptor(cmd *cobra.Command, filename string) (string, error) {
170169
)
171170

172171
if filename == "-" {
173-
reader = ioutil.NopCloser(cmd.InOrStdin())
172+
reader = io.NopCloser(cmd.InOrStdin())
174173
} else {
175174
reader, err = os.Open(filename)
176175
if err != nil {
@@ -179,7 +178,7 @@ func readDescriptor(cmd *cobra.Command, filename string) (string, error) {
179178
}
180179
defer reader.Close()
181180

182-
buf, err := ioutil.ReadAll(reader)
181+
buf, err := io.ReadAll(reader)
183182
if err != nil {
184183
return "", err
185184
}

pkg/commands/waiter.go

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"k8s.io/apimachinery/pkg/runtime/schema"
1717
"k8s.io/apimachinery/pkg/watch"
1818
"k8s.io/client-go/dynamic"
19+
"k8s.io/client-go/tools/cache"
1920
watchTools "k8s.io/client-go/tools/watch"
2021
"knative.dev/pkg/apis"
2122
"knative.dev/pkg/apis/duck"
@@ -59,18 +60,24 @@ func (w *Waiter) wait(ctx context.Context, ob runtime.Object, condition watchToo
5960
return err
6061
}
6162

62-
if !done {
63-
refable, ok := ob.(kmeta.OwnerRefable)
64-
if !ok {
65-
return errors.New("unexpected type")
66-
}
63+
refable, ok := ob.(kmeta.OwnerRefable)
64+
if !ok {
65+
return errors.New("unexpected type")
66+
}
6767

68-
rv := refable.GetObjectMeta().GetResourceVersion()
69-
watchOne := newWatchOneWatcher(ctx, refable, w.dynamicClient)
68+
listerWatcherOne := newWatchOneWatcher(ctx, refable, w.dynamicClient)
7069

71-
ctx, cancel := context.WithTimeout(context.Background(), w.timeout)
72-
defer cancel()
73-
e, err = watchTools.Until(ctx, rv, watchOne, filterErrors(cfs)...)
70+
ctx, cancel := context.WithTimeout(context.Background(), w.timeout)
71+
defer cancel()
72+
73+
if !done {
74+
e, err = watchTools.UntilWithSync(ctx,
75+
&listerWatcherOne,
76+
&unstructured.Unstructured{},
77+
func(store cache.Store) (bool, error) {
78+
return false, nil
79+
},
80+
filterErrors(cfs)...)
7481
if err != nil {
7582
return err
7683
}
@@ -158,6 +165,11 @@ func (w watchOneWatcher) Watch(options metav1.ListOptions) (watch.Interface, err
158165
return w.dynamicClient.Resource(w.gvr).Namespace(w.namespace).Watch(w.ctx, options)
159166
}
160167

168+
func (w watchOneWatcher) List(options metav1.ListOptions) (runtime.Object, error) {
169+
options.FieldSelector = fmt.Sprintf("metadata.name=%s", w.name)
170+
return w.dynamicClient.Resource(w.gvr).Namespace(w.namespace).List(w.ctx, options)
171+
}
172+
161173
func filterErrors(conditions []watchTools.ConditionFunc) []watchTools.ConditionFunc {
162174
cfs := []watchTools.ConditionFunc{}
163175
for _, c := range conditions {

pkg/commands/waiter_test.go

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"github.com/stretchr/testify/require"
1616
corev1 "k8s.io/api/core/v1"
1717
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
19+
"k8s.io/apimachinery/pkg/runtime"
1820
"k8s.io/apimachinery/pkg/watch"
1921
dynamicfake "k8s.io/client-go/dynamic/fake"
2022
"k8s.io/client-go/kubernetes/scheme"
@@ -81,13 +83,19 @@ func testWaiter(t *testing.T, when spec.G, it spec.S) {
8183
Status: conditionReady(corev1.ConditionFalse, generation-1),
8284
}
8385

86+
builderObj := &v1alpha2.Builder{
87+
TypeMeta: resourceToWatch.TypeMeta,
88+
ObjectMeta: resourceToWatch.ObjectMeta,
89+
Status: v1alpha2.BuilderStatus{Status: conditionReady(corev1.ConditionTrue, generation)},
90+
}
91+
92+
content, err := runtime.DefaultUnstructuredConverter.ToUnstructured(builderObj)
93+
if err != nil {
94+
panic(err)
95+
}
8496
watcher.addEvent(watch.Event{
85-
Type: watch.Modified,
86-
Object: &v1alpha2.Builder{
87-
TypeMeta: resourceToWatch.TypeMeta,
88-
ObjectMeta: resourceToWatch.ObjectMeta,
89-
Status: v1alpha2.BuilderStatus{Status: conditionReady(corev1.ConditionTrue, generation)},
90-
},
97+
Type: watch.Modified,
98+
Object: &unstructured.Unstructured{Object: content},
9199
})
92100

93101
require.NoError(t, waiter.Wait(context.Background(), resourceToWatch))
@@ -99,18 +107,56 @@ func testWaiter(t *testing.T, when spec.G, it spec.S) {
99107
Status: conditionReady(corev1.ConditionFalse, generation-1),
100108
}
101109

110+
builderObj := &v1alpha2.Builder{
111+
TypeMeta: resourceToWatch.TypeMeta,
112+
ObjectMeta: resourceToWatch.ObjectMeta,
113+
Status: v1alpha2.BuilderStatus{Status: conditionReady(corev1.ConditionTrue, generation)},
114+
}
115+
116+
content, err := runtime.DefaultUnstructuredConverter.ToUnstructured(builderObj)
117+
if err != nil {
118+
panic(err)
119+
}
102120
watcher.addEvent(watch.Event{
103-
Type: watch.Modified,
104-
Object: &v1alpha2.Builder{
105-
TypeMeta: resourceToWatch.TypeMeta,
106-
ObjectMeta: resourceToWatch.ObjectMeta,
107-
Status: v1alpha2.BuilderStatus{Status: conditionReady(corev1.ConditionTrue, generation)},
108-
},
121+
Type: watch.Modified,
122+
Object: &unstructured.Unstructured{Object: content},
109123
})
110124

111125
require.NoError(t, waiter.Wait(context.Background(), resourceToWatch, fakeConditionChecker.conditionCheck))
112126
require.True(t, fakeConditionChecker.called)
113127
})
128+
129+
it("recovers from too old resource version error", func() {
130+
watcher.addEvent(watch.Event{
131+
Type: watch.Error,
132+
Object: &v1.Status{
133+
TypeMeta: v1.TypeMeta{
134+
APIVersion: "v1",
135+
Kind: "Status",
136+
},
137+
Status: "Failure",
138+
Message: "too old resource version: 23358 (23360)",
139+
Reason: "Expired",
140+
Code: 410,
141+
},
142+
})
143+
144+
builderObj := &v1alpha2.Builder{
145+
TypeMeta: resourceToWatch.TypeMeta,
146+
ObjectMeta: resourceToWatch.ObjectMeta,
147+
Status: v1alpha2.BuilderStatus{Status: conditionReady(corev1.ConditionTrue, generation)},
148+
}
149+
content, err := runtime.DefaultUnstructuredConverter.ToUnstructured(builderObj)
150+
if err != nil {
151+
panic(err)
152+
}
153+
watcher.addEvent(watch.Event{
154+
Type: watch.Modified,
155+
Object: &unstructured.Unstructured{Object: content},
156+
})
157+
158+
require.NoError(t, waiter.Wait(context.Background(), resourceToWatch))
159+
})
114160
})
115161
}
116162

@@ -121,7 +167,6 @@ type fakeConditionChecker struct {
121167
func (cc *fakeConditionChecker) conditionCheck(_ watch.Event) (bool, error) {
122168
cc.called = true
123169
return true, nil
124-
125170
}
126171

127172
func conditionReady(status corev1.ConditionStatus, generation int64) corev1alpha1.Status {
@@ -159,10 +204,6 @@ func (t *TestWatcher) watchReactor(action clientgotesting.Action) (handled bool,
159204
}
160205

161206
watchAction := action.(clientgotesting.WatchAction)
162-
if watchAction.GetWatchRestrictions().ResourceVersion != t.expectedResource.GetObjectMeta().GetResourceVersion() {
163-
return true, nil, errors.New("expected watch on resource version")
164-
}
165-
166207
if watchAction.GetNamespace() != t.expectedResource.GetObjectMeta().GetNamespace() {
167208
return true, nil, errors.New("expected watch on namespace")
168209
}

0 commit comments

Comments
 (0)