Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/integrations/v2/controller_httpintegration_test.go
5287 views
1
package integrations
2
3
import (
4
"fmt"
5
"io"
6
"net/http"
7
"net/http/httptest"
8
"strings"
9
"testing"
10
11
"github.com/go-kit/log"
12
"github.com/gorilla/mux"
13
"github.com/grafana/agent/pkg/util"
14
"github.com/stretchr/testify/require"
15
)
16
17
//
18
// Tests for controller's utilization of the HTTPIntegration interface.
19
//
20
21
func Test_controller_Handler_Sync(t *testing.T) {
22
httpConfigFromID := func(t *testing.T, name, identifier string) Config {
23
t.Helper()
24
25
cfg := mockConfigNameTuple(t, name, identifier)
26
cfg.NewIntegrationFunc = func(log.Logger, Globals) (Integration, error) {
27
i := mockHTTPIntegration{
28
Integration: NoOpIntegration,
29
HandlerFunc: func(prefix string) (http.Handler, error) {
30
return http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
31
// We should never reach here since we don't run the integrations.
32
rw.WriteHeader(http.StatusBadRequest)
33
}), nil
34
},
35
}
36
return i, nil
37
}
38
39
return cfg
40
}
41
42
cfg := controllerConfig{httpConfigFromID(t, "foo", "bar")}
43
ctrl, err := newController(util.TestLogger(t), cfg, Globals{})
44
require.NoError(t, err)
45
46
handler, err := ctrl.Handler("/integrations/")
47
require.NoError(t, err)
48
49
srv := httptest.NewServer(handler)
50
51
resp, err := srv.Client().Get(srv.URL + "/integrations/foo/bar")
52
require.NoError(t, err)
53
require.Equal(t, http.StatusServiceUnavailable, resp.StatusCode)
54
}
55
56
// Test_controller_HTTPIntegration_Prefixes ensures that the controller will assign
57
// appropriate prefixes to HTTPIntegrations.
58
func Test_controller_HTTPIntegration_Prefixes(t *testing.T) {
59
httpConfigFromID := func(t *testing.T, prefixes *[]string, name, identifier string) Config {
60
t.Helper()
61
62
cfg := mockConfigNameTuple(t, name, identifier)
63
cfg.NewIntegrationFunc = func(log.Logger, Globals) (Integration, error) {
64
i := mockHTTPIntegration{
65
Integration: NoOpIntegration,
66
HandlerFunc: func(prefix string) (http.Handler, error) {
67
*prefixes = append(*prefixes, prefix)
68
return http.NotFoundHandler(), nil
69
},
70
}
71
return i, nil
72
}
73
74
return cfg
75
}
76
77
t.Run("fully unique", func(t *testing.T) {
78
var prefixes []string
79
80
ctrl, err := newController(
81
util.TestLogger(t),
82
controllerConfig{
83
httpConfigFromID(t, &prefixes, "foo", "bar"),
84
httpConfigFromID(t, &prefixes, "fizz", "buzz"),
85
httpConfigFromID(t, &prefixes, "hello", "world"),
86
},
87
Globals{},
88
)
89
require.NoError(t, err)
90
_ = newSyncController(t, ctrl)
91
92
_, err = ctrl.Handler("/integrations/")
93
require.NoError(t, err)
94
95
expect := []string{
96
"/integrations/foo/",
97
"/integrations/fizz/",
98
"/integrations/hello/",
99
}
100
require.ElementsMatch(t, prefixes, expect)
101
})
102
103
t.Run("multiple instances", func(t *testing.T) {
104
var prefixes []string
105
106
ctrl, err := newController(
107
util.TestLogger(t),
108
controllerConfig{
109
httpConfigFromID(t, &prefixes, "foo", "bar"),
110
httpConfigFromID(t, &prefixes, "foo", "buzz"),
111
httpConfigFromID(t, &prefixes, "hello", "world"),
112
},
113
Globals{},
114
)
115
require.NoError(t, err)
116
_ = newSyncController(t, ctrl)
117
118
_, err = ctrl.Handler("/integrations/")
119
require.NoError(t, err)
120
121
expect := []string{
122
"/integrations/foo/bar/",
123
"/integrations/foo/buzz/",
124
"/integrations/hello/",
125
}
126
require.ElementsMatch(t, prefixes, expect)
127
})
128
}
129
130
// Test_controller_HTTPIntegration_Routing ensures that the controller will route
131
// requests to the appropriate integration.
132
func Test_controller_HTTPIntegration_Routing(t *testing.T) {
133
httpConfigFromID := func(t *testing.T, name, identifier string) Config {
134
t.Helper()
135
136
cfg := mockConfigNameTuple(t, name, identifier)
137
cfg.NewIntegrationFunc = func(log.Logger, Globals) (Integration, error) {
138
i := mockHTTPIntegration{
139
Integration: NoOpIntegration,
140
HandlerFunc: func(prefix string) (http.Handler, error) {
141
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
142
fmt.Fprintf(rw, "prefix=%s, path=%s", prefix, r.URL.Path)
143
}), nil
144
},
145
}
146
return i, nil
147
}
148
149
return cfg
150
}
151
152
ctrl, err := newController(
153
util.TestLogger(t),
154
controllerConfig{
155
httpConfigFromID(t, "foo", "bar"),
156
httpConfigFromID(t, "foo", "buzz"),
157
httpConfigFromID(t, "hello", "world"),
158
},
159
Globals{},
160
)
161
require.NoError(t, err)
162
_ = newSyncController(t, ctrl)
163
164
handler, err := ctrl.Handler("/integrations/")
165
require.NoError(t, err)
166
167
srv := httptest.NewServer(handler)
168
169
getResponse := func(t *testing.T, path string) string {
170
t.Helper()
171
resp, err := srv.Client().Get(srv.URL + path)
172
require.NoError(t, err)
173
defer resp.Body.Close()
174
175
var sb strings.Builder
176
_, err = io.Copy(&sb, resp.Body)
177
require.NoError(t, err)
178
return sb.String()
179
}
180
181
tt := []struct {
182
path, expect string
183
}{
184
{"/integrations/foo/bar", "prefix=/integrations/foo/bar/, path=/integrations/foo/bar"},
185
{"/integrations/foo/bar/", "prefix=/integrations/foo/bar/, path=/integrations/foo/bar/"},
186
{"/integrations/foo/bar/extra", "prefix=/integrations/foo/bar/, path=/integrations/foo/bar/extra"},
187
}
188
189
for _, tc := range tt {
190
require.Equal(t, tc.expect, getResponse(t, tc.path))
191
}
192
}
193
194
// Test_controller_HTTPIntegration_NestedRouting ensures that the controller
195
// will work with nested routers.
196
func Test_controller_HTTPIntegration_NestedRouting(t *testing.T) {
197
cfg := mockConfigNameTuple(t, "test", "test")
198
cfg.NewIntegrationFunc = func(log.Logger, Globals) (Integration, error) {
199
i := mockHTTPIntegration{
200
Integration: NoOpIntegration,
201
HandlerFunc: func(prefix string) (http.Handler, error) {
202
r := mux.NewRouter()
203
r.StrictSlash(true)
204
205
r.HandleFunc(prefix, func(rw http.ResponseWriter, r *http.Request) {
206
fmt.Fprintf(rw, "prefix=%s, path=%s", prefix, r.URL.Path)
207
})
208
209
r.HandleFunc(prefix+"greet", func(rw http.ResponseWriter, _ *http.Request) {
210
fmt.Fprintf(rw, "Hello, world!")
211
})
212
return r, nil
213
},
214
}
215
return i, nil
216
}
217
218
ctrl, err := newController(util.TestLogger(t), controllerConfig{cfg}, Globals{})
219
require.NoError(t, err)
220
_ = newSyncController(t, ctrl)
221
222
handler, err := ctrl.Handler("/integrations/")
223
require.NoError(t, err)
224
225
srv := httptest.NewServer(handler)
226
227
getResponse := func(t *testing.T, path string) string {
228
t.Helper()
229
resp, err := srv.Client().Get(srv.URL + path)
230
require.NoError(t, err)
231
defer resp.Body.Close()
232
233
var sb strings.Builder
234
_, err = io.Copy(&sb, resp.Body)
235
require.NoError(t, err)
236
return sb.String()
237
}
238
239
tt := []struct {
240
path, expect string
241
}{
242
{"/integrations/test", "prefix=/integrations/test/, path=/integrations/test/"},
243
{"/integrations/test/", "prefix=/integrations/test/, path=/integrations/test/"},
244
{"/integrations/test/greet", "Hello, world!"},
245
}
246
247
for _, tc := range tt {
248
require.Equal(t, tc.expect, getResponse(t, tc.path))
249
}
250
}
251
252
type mockHTTPIntegration struct {
253
Integration
254
HandlerFunc func(prefix string) (http.Handler, error)
255
}
256
257
func (m mockHTTPIntegration) Handler(prefix string) (http.Handler, error) {
258
return m.HandlerFunc(prefix)
259
}
260
261