Skip to content
This repository was archived by the owner on Jun 26, 2024. It is now read-only.

Commit fe3abe1

Browse files
authored
handle panics occurring in reconsile pipeline gracefuly (#1032)
If a panic happens while reconciling service binding, put the binding back in the queue and retry again Signed-off-by: Predrag Knezevic <[email protected]>
1 parent bda0854 commit fe3abe1

File tree

2 files changed

+66
-2
lines changed

2 files changed

+66
-2
lines changed

pkg/reconcile/pipeline/builder/pipeline.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package builder
22

33
import (
4+
"fmt"
45
"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline"
56
"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/handler/collect"
67
"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/handler/mapping"
@@ -15,14 +16,20 @@ type impl struct {
1516
handlers []pipeline.Handler
1617
}
1718

18-
func (i *impl) Process(binding interface{}) (bool, error) {
19+
func (i *impl) Process(binding interface{}) (retry bool, err error) {
20+
defer func() {
21+
if perr := recover(); perr != nil {
22+
retry = true
23+
err = fmt.Errorf("panic occurred: %v", perr)
24+
}
25+
}()
1926
ctx, err := i.ctxProvider.Get(binding)
2027
if err != nil {
2128
return false, err
2229
}
2330
var status pipeline.FlowStatus
2431
for _, h := range i.handlers {
25-
h.Handle(ctx)
32+
invokeHandler(h, ctx)
2633
status = ctx.FlowStatus()
2734
if status.Stop {
2835
break
@@ -61,6 +68,15 @@ func Builder() *builder {
6168
return &builder{}
6269
}
6370

71+
func invokeHandler(h pipeline.Handler, ctx pipeline.Context) {
72+
defer func() {
73+
if err := recover(); err != nil {
74+
ctx.RetryProcessing(fmt.Errorf("panic occurred: %v", err))
75+
}
76+
}()
77+
h.Handle(ctx)
78+
}
79+
6480
var defaultFlow = []pipeline.Handler{
6581
pipeline.HandlerFunc(project.Unbind),
6682
pipeline.HandlerFunc(collect.PreFlight),

pkg/reconcile/pipeline/builder/pipeline_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package builder_test
22

33
import (
4+
"fmt"
45
"github.com/golang/mock/gomock"
56
"github.com/pkg/errors"
67
"github.com/redhat-developer/service-binding-operator/apis/binding/v1alpha1"
@@ -82,6 +83,27 @@ var _ = Describe("Pipeline", func() {
8283
Expect(retry).To(BeTrue())
8384
})
8485

86+
It("should retry processing if panic occurred in a handler", func() {
87+
err := fmt.Errorf("panic occurred: %v", "foo")
88+
89+
h1 := defHandler()
90+
h1.EXPECT().Handle(ctx)
91+
h2 := func(c pipeline.Context) {
92+
panic("foo")
93+
}
94+
h3 := defHandler()
95+
p := builder.Builder().WithContextProvider(&ctxProvider{ctx: ctx}).WithHandlers(h1, pipeline.HandlerFunc(h2), h3).Build()
96+
97+
ctx.EXPECT().RetryProcessing(err)
98+
ctx.EXPECT().Close().Return(nil)
99+
ctx.EXPECT().FlowStatus().Return(pipeline.FlowStatus{})
100+
ctx.EXPECT().FlowStatus().Return(pipeline.FlowStatus{Retry: true, Stop: true, Err: err})
101+
102+
retry, rerr := p.Process(&v1alpha1.ServiceBinding{})
103+
Expect(rerr).To(Equal(err))
104+
Expect(retry).To(BeTrue())
105+
})
106+
85107
It("should stop without retry and error and propagate that back to caller", func() {
86108
h1 := defHandler()
87109
h1.EXPECT().Handle(ctx)
@@ -116,6 +138,32 @@ var _ = Describe("Pipeline", func() {
116138
Expect(err).To(Equal(err))
117139
Expect(retry).To(BeTrue())
118140
})
141+
142+
It("should retry processing if panic occurs when closing context", func() {
143+
var err = fmt.Errorf("panic occurred: %v", "foo")
144+
h1 := defHandler()
145+
h1.EXPECT().Handle(ctx)
146+
p := builder.Builder().WithContextProvider(&ctxProvider{ctx: ctx}).WithHandlers(h1).Build()
147+
148+
ctx.EXPECT().Close().DoAndReturn(func() { panic("foo") })
149+
ctx.EXPECT().FlowStatus().Return(pipeline.FlowStatus{}).Times(1)
150+
151+
retry, perr := p.Process(&v1alpha1.ServiceBinding{})
152+
Expect(perr).To(Equal(err))
153+
Expect(retry).To(BeTrue())
154+
})
155+
156+
It("should retry processing if panic occurs when calling context provider", func() {
157+
var err = fmt.Errorf("panic occurred: %v", "foo")
158+
h1 := defHandler()
159+
provider := mocks.NewMockContextProvider(mockCtrl)
160+
provider.EXPECT().Get(&v1alpha1.ServiceBinding{}).DoAndReturn(func(b interface{}) { panic("foo") })
161+
p := builder.Builder().WithContextProvider(provider).WithHandlers(h1).Build()
162+
163+
retry, perr := p.Process(&v1alpha1.ServiceBinding{})
164+
Expect(perr).To(Equal(err))
165+
Expect(retry).To(BeTrue())
166+
})
119167
})
120168

121169
type ctxProvider struct {

0 commit comments

Comments
 (0)