Skip to content

Commit 5c72e84

Browse files
author
Paulo Gomes
committed
Refactor tests for blocking HTTP connections
- Use noopstore to disable throttling behaviour. - Fake k8s client to remove need of interacting with an envtest apiserver. - Replace HTTP Status Code magic numbers, with their respective constants. Signed-off-by: Paulo Gomes <[email protected]>
1 parent 8ea5d6c commit 5c72e84

File tree

5 files changed

+145
-87
lines changed

5 files changed

+145
-87
lines changed

controllers/event_handling_test.go

Lines changed: 8 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@ import (
77
"fmt"
88
"net/http"
99
"net/http/httptest"
10-
"net/url"
1110
"testing"
1211
"time"
1312

1413
"github.com/fluxcd/pkg/ssa"
1514
. "github.com/onsi/gomega"
16-
"github.com/sethvargo/go-limiter/memorystore"
15+
"github.com/sethvargo/go-limiter/noopstore"
1716
prommetrics "github.com/slok/go-http-metrics/metrics/prometheus"
1817
"github.com/slok/go-http-metrics/middleware"
1918
corev1 "k8s.io/api/core/v1"
@@ -46,82 +45,17 @@ func TestEventHandler(t *testing.T) {
4645
}),
4746
})
4847

49-
store, err := memorystore.New(&memorystore.Config{
50-
Interval: 5 * time.Minute,
51-
})
52-
if err != nil {
53-
t.Fatalf("failed to create memory storage")
54-
}
55-
56-
httpScheme := "http"
57-
58-
eventServerTests := []struct {
59-
name string
60-
isHttpEnabled bool
61-
url string
62-
}{
63-
{
64-
name: "http scheme is enabled",
65-
isHttpEnabled: true,
66-
}, {
67-
name: "http scheme is disabled",
68-
isHttpEnabled: false,
69-
},
70-
}
71-
for _, eventServerTest := range eventServerTests {
72-
t.Run(eventServerTest.name, func(t *testing.T) {
73-
74-
eventServer := server.NewEventServer("127.0.0.1:56789", logf.Log, k8sClient, true, eventServerTest.isHttpEnabled)
75-
76-
stopCh := make(chan struct{})
77-
go eventServer.ListenAndServe(stopCh, eventMdlw, store)
78-
requestsReceived := 0
79-
rcvServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
80-
requestsReceived = requestsReceived + 1
81-
req = r
82-
w.WriteHeader(200)
83-
}))
84-
defer rcvServer.Close()
85-
defer close(stopCh)
86-
87-
providerKey := types.NamespacedName{
88-
Name: fmt.Sprintf("provider-%s", randStringRunes(5)),
89-
Namespace: namespace,
90-
}
91-
provider = &notifyv1.Provider{
92-
ObjectMeta: metav1.ObjectMeta{
93-
Name: providerKey.Name,
94-
Namespace: providerKey.Namespace,
95-
},
96-
Spec: notifyv1.ProviderSpec{
97-
Type: "generic",
98-
Address: rcvServer.URL,
99-
},
100-
}
101-
102-
webhook_url, err := url.Parse(provider.Spec.Address)
103-
104-
g.Expect(err).ToNot(HaveOccurred())
105-
106-
if eventServerTest.isHttpEnabled {
107-
g.Expect(webhook_url.Scheme).To(Equal(httpScheme))
108-
g.Expect(requestsReceived).To(Equal(1))
109-
} else {
110-
g.Expect(webhook_url.Scheme).ToNot(Equal(httpScheme))
111-
g.Expect(requestsReceived).To(Equal(0))
112-
}
113-
114-
})
115-
}
116-
117-
eventServer := server.NewEventServer("127.0.0.1:56789", logf.Log, k8sClient, true, true)
48+
store, err := noopstore.New()
49+
g.Expect(err).ToNot(HaveOccurred())
11850

51+
serverEndpoint := "127.0.0.1:56789"
52+
eventServer := server.NewEventServer(serverEndpoint, logf.Log, k8sClient, true, true)
11953
stopCh := make(chan struct{})
12054
go eventServer.ListenAndServe(stopCh, eventMdlw, store)
12155

12256
rcvServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
12357
req = r
124-
w.WriteHeader(200)
58+
w.WriteHeader(http.StatusOK)
12559
}))
12660
defer rcvServer.Close()
12761
defer close(stopCh)
@@ -236,9 +170,9 @@ func TestEventHandler(t *testing.T) {
236170
testSent := func() {
237171
buf := &bytes.Buffer{}
238172
g.Expect(json.NewEncoder(buf).Encode(&event)).To(Succeed())
239-
res, err := http.Post("http://localhost:56789/", "application/json", buf)
173+
res, err := http.Post("http://"+serverEndpoint, "application/json", buf)
240174
g.Expect(err).ToNot(HaveOccurred())
241-
g.Expect(res.StatusCode).To(Equal(202)) // event_server responds with 202 Accepted
175+
g.Expect(res.StatusCode).To(Equal(http.StatusAccepted))
242176

243177
}
244178

@@ -361,5 +295,4 @@ func TestEventHandler(t *testing.T) {
361295
req = nil
362296
})
363297
}
364-
365298
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ require (
3131
k8s.io/api v0.25.3
3232
k8s.io/apimachinery v0.25.3
3333
k8s.io/client-go v0.25.3
34+
k8s.io/kubectl v0.24.0
3435
sigs.k8s.io/cli-utils v0.33.0
3536
sigs.k8s.io/controller-runtime v0.13.0
3637
sigs.k8s.io/yaml v1.3.0
@@ -151,7 +152,6 @@ require (
151152
k8s.io/component-base v0.25.2 // indirect
152153
k8s.io/klog/v2 v2.80.1 // indirect
153154
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
154-
k8s.io/kubectl v0.24.0 // indirect
155155
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
156156
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
157157
sigs.k8s.io/kustomize/api v0.12.1 // indirect

internal/server/event_handlers.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ import (
4545

4646
func (s *EventServer) handleEvent() func(w http.ResponseWriter, r *http.Request) {
4747
return func(w http.ResponseWriter, r *http.Request) {
48-
r.Context()
4948
body, err := io.ReadAll(r.Body)
5049
if err != nil {
5150
s.logger.Error(err, "reading the request body failed")

internal/server/event_server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func (s *EventServer) logRateLimitMiddleware(h http.Handler) http.Handler {
106106
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
107107
recorder := &statusRecorder{
108108
ResponseWriter: w,
109-
Status: 200,
109+
Status: http.StatusOK,
110110
}
111111
h.ServeHTTP(recorder, r)
112112

internal/server/event_server_test.go

Lines changed: 135 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,39 @@ import (
2020
"bytes"
2121
"encoding/json"
2222
"fmt"
23+
"io"
2324
"net/http"
2425
"net/http/httptest"
2526
"testing"
2627
"time"
2728

28-
"github.com/onsi/gomega"
29+
. "github.com/onsi/gomega"
2930
"github.com/sethvargo/go-limiter/httplimit"
3031
"github.com/sethvargo/go-limiter/memorystore"
32+
"github.com/sethvargo/go-limiter/noopstore"
33+
"github.com/slok/go-http-metrics/middleware"
3134
corev1 "k8s.io/api/core/v1"
35+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
36+
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
37+
"k8s.io/kubectl/pkg/scheme"
38+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
39+
logf "sigs.k8s.io/controller-runtime/pkg/log"
3240

41+
notifyv1 "github.com/fluxcd/notification-controller/api/v1beta1"
42+
"github.com/fluxcd/pkg/apis/meta"
3343
"github.com/fluxcd/pkg/runtime/events"
3444
)
3545

3646
func TestEventKeyFunc(t *testing.T) {
37-
g := gomega.NewGomegaWithT(t)
47+
g := NewWithT(t)
3848

3949
// Setup middleware
4050
store, err := memorystore.New(&memorystore.Config{
4151
Interval: 10 * time.Minute,
4252
})
43-
g.Expect(err).ShouldNot(gomega.HaveOccurred())
53+
g.Expect(err).ShouldNot(HaveOccurred())
4454
middleware, err := httplimit.NewMiddleware(store, eventKeyFunc)
45-
g.Expect(err).ShouldNot(gomega.HaveOccurred())
55+
g.Expect(err).ShouldNot(HaveOccurred())
4656
handler := middleware.Handle(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
4757
w.WriteHeader(http.StatusOK)
4858
}))
@@ -129,19 +139,135 @@ func TestEventKeyFunc(t *testing.T) {
129139
Message: tt.message,
130140
}
131141
eventData, err := json.Marshal(event)
132-
g.Expect(err).ShouldNot(gomega.HaveOccurred())
142+
g.Expect(err).ShouldNot(HaveOccurred())
133143

134144
req := httptest.NewRequest("POST", "/", bytes.NewBuffer(eventData))
135-
g.Expect(err).ShouldNot(gomega.HaveOccurred())
145+
g.Expect(err).ShouldNot(HaveOccurred())
136146
res := httptest.NewRecorder()
137147
handler.ServeHTTP(res, req)
138148

139149
if tt.rateLimit {
140-
g.Expect(res.Code).Should(gomega.Equal(429))
141-
g.Expect(res.Header().Get("X-Ratelimit-Remaining")).Should(gomega.Equal("0"))
150+
g.Expect(res.Code).Should(Equal(http.StatusTooManyRequests))
151+
g.Expect(res.Header().Get("X-Ratelimit-Remaining")).Should(Equal("0"))
142152
} else {
143-
g.Expect(res.Code).Should(gomega.Equal(200))
153+
g.Expect(res.Code).Should(Equal(http.StatusOK))
144154
}
145155
})
146156
}
147157
}
158+
159+
func TestBlockInsecureHTTP(t *testing.T) {
160+
g := NewWithT(t)
161+
162+
var requestsReceived int
163+
rcvServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
164+
requestsReceived++
165+
io.Copy(io.Discard, r.Body)
166+
w.WriteHeader(http.StatusOK)
167+
}))
168+
defer rcvServer.Close()
169+
170+
utilruntime.Must(notifyv1.AddToScheme(scheme.Scheme))
171+
172+
testNamespace := "test-ns"
173+
providerKey := "provider"
174+
client := fake.NewFakeClientWithScheme(scheme.Scheme,
175+
&notifyv1.Provider{
176+
ObjectMeta: metav1.ObjectMeta{
177+
Name: providerKey,
178+
Namespace: testNamespace,
179+
},
180+
Spec: notifyv1.ProviderSpec{
181+
Type: "generic",
182+
Address: rcvServer.URL,
183+
},
184+
},
185+
&notifyv1.Alert{
186+
ObjectMeta: metav1.ObjectMeta{
187+
Name: "some-alert-name",
188+
Namespace: testNamespace,
189+
},
190+
Spec: notifyv1.AlertSpec{
191+
ProviderRef: meta.LocalObjectReference{
192+
Name: providerKey,
193+
},
194+
EventSeverity: "info",
195+
EventSources: []notifyv1.CrossNamespaceObjectReference{
196+
{
197+
Kind: "Bucket",
198+
Name: "hyacinth",
199+
Namespace: testNamespace,
200+
},
201+
},
202+
},
203+
Status: notifyv1.AlertStatus{
204+
Conditions: []metav1.Condition{
205+
{Type: meta.ReadyCondition, Status: metav1.ConditionTrue},
206+
},
207+
},
208+
},
209+
)
210+
211+
eventMdlw := middleware.New(middleware.Config{})
212+
213+
store, err := noopstore.New()
214+
g.Expect(err).ToNot(HaveOccurred())
215+
216+
serverEndpoint := "127.0.0.1:56789"
217+
eventServer := NewEventServer(serverEndpoint, logf.Log, client, true, true)
218+
stopCh := make(chan struct{})
219+
go eventServer.ListenAndServe(stopCh, eventMdlw, store)
220+
defer close(stopCh)
221+
222+
event := events.Event{
223+
InvolvedObject: corev1.ObjectReference{
224+
Kind: "Bucket",
225+
Name: "hyacinth",
226+
Namespace: testNamespace,
227+
},
228+
Severity: "info",
229+
Timestamp: metav1.Now(),
230+
Message: "well that happened",
231+
Reason: "event-happened",
232+
ReportingController: "source-controller",
233+
}
234+
235+
eventServerTests := []struct {
236+
name string
237+
isHttpEnabled bool
238+
url string
239+
wantRequest int
240+
}{
241+
{
242+
name: "http scheme is disabled",
243+
isHttpEnabled: false,
244+
wantRequest: 0,
245+
},
246+
{
247+
name: "http scheme is enabled",
248+
isHttpEnabled: true,
249+
wantRequest: 1,
250+
},
251+
}
252+
for _, tt := range eventServerTests {
253+
t.Run(tt.name, func(t *testing.T) {
254+
g := NewWithT(t)
255+
requestsReceived = 0 // reset counter
256+
257+
// Change the internal state instead of creating a new server.
258+
eventServer.supportHttpScheme = tt.isHttpEnabled
259+
260+
buf := &bytes.Buffer{}
261+
g.Expect(json.NewEncoder(buf).Encode(&event)).To(Succeed())
262+
res, err := http.Post("http://"+serverEndpoint, "application/json", buf)
263+
264+
g.Expect(err).ToNot(HaveOccurred())
265+
g.Expect(res.StatusCode).To(Equal(http.StatusAccepted))
266+
267+
// Requests happens async, so should the assertion.
268+
g.Eventually(func() bool {
269+
return requestsReceived == tt.wantRequest
270+
}, 5*time.Second).Should(BeTrue())
271+
})
272+
}
273+
}

0 commit comments

Comments
 (0)