Skip to content
This repository was archived by the owner on Dec 21, 2023. It is now read-only.

Commit 2052e14

Browse files
authored
feat: Added option for configuring number of retries for http event sender, as well as additional logging (#465)
* feat: Added option for configuring number of retries for http event sender, as well as additional logging Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com> * added option for injecting retry callback Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com> * added option for injecting retry callback Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com> * remove retry callback option Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com> * remove retry callback option unit test Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com> * added comment Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com>
1 parent c5e1b75 commit 2052e14

File tree

2 files changed

+59
-38
lines changed

2 files changed

+59
-38
lines changed

pkg/lib/v0_2_0/events.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,27 @@ const keptnSpecVersionCEExtension = "shkeptnspecversion"
4141
const triggeredIDCEExtension = "triggeredid"
4242
const keptnGitCommitIDCEExtension = "gitcommitid"
4343

44+
type HTTPSenderOption func(httpSender *HTTPEventSender)
45+
46+
// WithSendRetries allows to specify the number of retries that are performed if the receiver of an event returns a HTTP error code
47+
func WithSendRetries(retries int) HTTPSenderOption {
48+
return func(httpSender *HTTPEventSender) {
49+
httpSender.nrRetries = retries
50+
}
51+
}
52+
4453
// HTTPEventSender sends CloudEvents via HTTP
4554
type HTTPEventSender struct {
4655
// EventsEndpoint is the http endpoint the events are sent to
4756
EventsEndpoint string
4857
// Client is an implementation of the cloudevents.Client interface
4958
Client cloudevents.Client
59+
// nrRetries is the number of retries that are attempted if the endpoint an event is forwarded to returns an http code outside the 2xx range
60+
nrRetries int
5061
}
5162

5263
// NewHTTPEventSender creates a new HTTPSender
53-
func NewHTTPEventSender(endpoint string) (*HTTPEventSender, error) {
64+
func NewHTTPEventSender(endpoint string, opts ...HTTPSenderOption) (*HTTPEventSender, error) {
5465
if endpoint == "" {
5566
endpoint = DefaultHTTPEventEndpoint
5667
}
@@ -75,6 +86,11 @@ func NewHTTPEventSender(endpoint string) (*HTTPEventSender, error) {
7586
httpSender := &HTTPEventSender{
7687
EventsEndpoint: endpoint,
7788
Client: c,
89+
nrRetries: MAX_SEND_RETRIES,
90+
}
91+
92+
for _, o := range opts {
93+
o(httpSender)
7894
}
7995
return httpSender, nil
8096
}
@@ -88,7 +104,7 @@ func (httpSender HTTPEventSender) Send(ctx context.Context, event cloudevents.Ev
88104
ctx = cloudevents.ContextWithTarget(ctx, httpSender.EventsEndpoint)
89105
ctx = cloudevents.WithEncodingStructured(ctx)
90106
var result protocol.Result
91-
for i := 0; i <= MAX_SEND_RETRIES; i++ {
107+
for i := 0; i <= httpSender.nrRetries; i++ {
92108
result = httpSender.Client.Send(ctx, event)
93109
httpResult, ok := result.(*httpprotocol.Result)
94110
switch {
@@ -103,7 +119,7 @@ func (httpSender HTTPEventSender) Send(ctx context.Context, event cloudevents.Ev
103119
return nil
104120
}
105121
}
106-
return errors.New("Failed to send cloudevent: " + result.Error())
122+
return fmt.Errorf("could not send cloudevent after %d retries. Received result from the receiver: %w", httpSender.nrRetries, result)
107123
}
108124

109125
// EventSender fakes the sending of CloudEvents

pkg/lib/v0_2_0/events_test.go

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package v0_2_0
22

33
import (
4+
"context"
45
"net/http"
56
"net/http/httptest"
67
"testing"
@@ -48,49 +49,53 @@ func TestKeptn_SendCloudEventWithRetry(t *testing.T) {
4849
failOnFirstTry = false
4950
w.WriteHeader(500)
5051
w.Write([]byte(`{}`))
52+
return
5153
}
5254
w.WriteHeader(200)
5355
w.Write([]byte(`{}`))
5456
}),
5557
)
5658
defer ts.Close()
5759

58-
type args struct {
59-
event cloudevents.Event
60-
}
61-
tests := []struct {
62-
name string
63-
fields fields
64-
args args
65-
wantErr bool
66-
}{
67-
{
68-
name: "",
69-
fields: getKeptnFields(ts),
70-
args: args{
71-
event: getTestEvent(),
72-
},
73-
wantErr: false,
74-
},
75-
}
76-
for _, tt := range tests {
77-
t.Run(tt.name, func(t *testing.T) {
78-
httpSender, _ := NewHTTPEventSender(ts.URL)
79-
k := &Keptn{
80-
KeptnBase: keptn.KeptnBase{
81-
KeptnContext: tt.fields.KeptnContext,
82-
Event: tt.fields.KeptnBase,
83-
EventSender: httpSender,
84-
UseLocalFileSystem: tt.fields.useLocalFileSystem,
85-
ResourceHandler: tt.fields.resourceHandler,
86-
EventHandler: tt.fields.eventHandler,
87-
},
88-
}
89-
if err := k.SendCloudEvent(tt.args.event); (err != nil) != tt.wantErr {
90-
t.Errorf("SendCloudEvent() error = %v, wantErr %v", err, tt.wantErr)
60+
httpSender, _ := NewHTTPEventSender(ts.URL)
61+
62+
err := httpSender.Send(context.TODO(), getTestEvent())
63+
64+
require.Nil(t, err)
65+
}
66+
67+
func TestKeptn_SendCloudEventWithOneRetry(t *testing.T) {
68+
nrRequests := 0
69+
ts := httptest.NewServer(
70+
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
71+
w.Header().Add("Content-Type", "application/json")
72+
if nrRequests < 2 {
73+
nrRequests++
74+
w.WriteHeader(500)
75+
w.Write([]byte(`{}`))
76+
return
9177
}
92-
})
93-
}
78+
w.WriteHeader(200)
79+
w.Write([]byte(`{}`))
80+
}),
81+
)
82+
defer ts.Close()
83+
84+
httpSender, _ := NewHTTPEventSender(ts.URL, WithSendRetries(1))
85+
86+
err := httpSender.Send(context.TODO(), getTestEvent())
87+
88+
require.NotNil(t, err)
89+
90+
// reset nrRequests
91+
nrRequests = 0
92+
93+
//initialize a new sender with 2 retries
94+
httpSender, _ = NewHTTPEventSender(ts.URL, WithSendRetries(5))
95+
96+
err = httpSender.Send(context.TODO(), getTestEvent())
97+
98+
require.Nil(t, err)
9499
}
95100

96101
func getTestEvent() cloudevents.Event {

0 commit comments

Comments
 (0)