1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3 #include <fluent-bit/flb_sds.h>
4 #include <fluent-bit/flb_aws_credentials.h>
5 #include <fluent-bit/flb_mem.h>
6 #include <fluent-bit/flb_info.h>
7 #include <fluent-bit/flb_http_client.h>
8
9 #include <monkey/mk_core.h>
10 #include <string.h>
11 #include <unistd.h>
12
13 #include "flb_tests_internal.h"
14
15 #define ACCESS_KEY_HTTP "http_akid"
16 #define SECRET_KEY_HTTP "http_skid"
17 #define TOKEN_HTTP "http_token"
18
19 #define HTTP_CREDENTIALS_RESPONSE "{\n\
20 \"AccessKeyId\": \"http_akid\",\n\
21 \"Expiration\": \"2025-10-24T23:00:23Z\",\n\
22 \"RoleArn\": \"TASK_ROLE_ARN\",\n\
23 \"SecretAccessKey\": \"http_skid\",\n\
24 \"Token\": \"http_token\"\n\
25 }"
26
27 /*
28 * Unexpected/invalid HTTP response. The goal of this is not to test anything
29 * that might happen in production, but rather to test the error handling
30 * code for the providers. This helps ensure all code paths are tested and
31 * the error handling code does not introduce memory leaks.
32 */
33 #define HTTP_RESPONSE_MALFORMED "{\n\
34 \"AccessKeyId\": \"http_akid\",\n\
35 \"partially-correct\": \"json\",\n\
36 \"RoleArn\": \"TASK_ROLE_ARN\",\n\
37 \"but incomplete\": \"and not terminated with a closing brace\",\n\
38 \"Token\": \"http_token\""
39
40
41 /*
42 * Global Variable that allows us to check the number of calls
43 * made in each test
44 */
45 int g_request_count;
46
request_happy_case(struct flb_aws_client * aws_client,int method,const char * uri)47 struct flb_http_client *request_happy_case(struct flb_aws_client *aws_client,
48 int method, const char *uri)
49 {
50 struct flb_http_client *c = NULL;
51
52 TEST_CHECK(method == FLB_HTTP_GET);
53
54 TEST_CHECK(strstr(uri, "happy-case") != NULL);
55
56 /* create an http client so that we can set the response */
57 c = flb_calloc(1, sizeof(struct flb_http_client));
58 if (!c) {
59 flb_errno();
60 return NULL;
61 }
62 mk_list_init(&c->headers);
63
64 c->resp.status = 200;
65 c->resp.payload = HTTP_CREDENTIALS_RESPONSE;
66 c->resp.payload_size = strlen(HTTP_CREDENTIALS_RESPONSE);
67
68 return c;
69 }
70
71 /* unexpected output test- see description for HTTP_RESPONSE_MALFORMED */
request_malformed(struct flb_aws_client * aws_client,int method,const char * uri)72 struct flb_http_client *request_malformed(struct flb_aws_client *aws_client,
73 int method, const char *uri)
74 {
75 struct flb_http_client *c = NULL;
76
77 TEST_CHECK(method == FLB_HTTP_GET);
78
79 TEST_CHECK(strstr(uri, "malformed") != NULL);
80
81 /* create an http client so that we can set the response */
82 c = flb_calloc(1, sizeof(struct flb_http_client));
83 if (!c) {
84 flb_errno();
85 return NULL;
86 }
87 mk_list_init(&c->headers);
88
89 c->resp.status = 200;
90 c->resp.payload = HTTP_RESPONSE_MALFORMED;
91 c->resp.payload_size = strlen(HTTP_RESPONSE_MALFORMED);
92
93 return c;
94 }
95
request_error_case(struct flb_aws_client * aws_client,int method,const char * uri)96 struct flb_http_client *request_error_case(struct flb_aws_client *aws_client,
97 int method, const char *uri)
98 {
99 struct flb_http_client *c = NULL;
100
101 TEST_CHECK(method == FLB_HTTP_GET);
102
103 TEST_CHECK(strstr(uri, "error-case") != NULL);
104
105 /* create an http client so that we can set the response */
106 c = flb_calloc(1, sizeof(struct flb_http_client));
107 if (!c) {
108 flb_errno();
109 return NULL;
110 }
111 mk_list_init(&c->headers);
112
113 c->resp.status = 400;
114 c->resp.payload = NULL;
115 c->resp.payload_size = 0;
116
117 return c;
118 }
119
120 /* test/mock version of the flb_aws_client request function */
test_http_client_request(struct flb_aws_client * aws_client,int method,const char * uri,const char * body,size_t body_len,struct flb_aws_header * dynamic_headers,size_t dynamic_headers_len)121 struct flb_http_client *test_http_client_request(struct flb_aws_client *aws_client,
122 int method, const char *uri,
123 const char *body, size_t body_len,
124 struct flb_aws_header *dynamic_headers,
125 size_t dynamic_headers_len)
126 {
127 g_request_count++;
128 /*
129 * route to the correct test case fn using the uri
130 */
131 if (strstr(uri, "happy-case") != NULL) {
132 return request_happy_case(aws_client, method, uri);
133 } else if (strstr(uri, "error-case") != NULL) {
134 return request_error_case(aws_client, method, uri);
135 } else if (strstr(uri, "malformed") != NULL) {
136 return request_malformed(aws_client, method, uri);
137 }
138
139 /* uri should match one of the above conditions */
140 flb_errno();
141 return NULL;
142
143 }
144
145 /* Test/mock flb_aws_client */
146 static struct flb_aws_client_vtable test_vtable = {
147 .request = test_http_client_request,
148 };
149
test_http_client_create()150 struct flb_aws_client *test_http_client_create()
151 {
152 struct flb_aws_client *client = flb_calloc(1,
153 sizeof(struct flb_aws_client));
154 if (!client) {
155 flb_errno();
156 return NULL;
157 }
158 client->client_vtable = &test_vtable;
159 return client;
160 }
161
162 /* Generator that returns clients with the test vtable */
163 static struct flb_aws_client_generator test_generator = {
164 .create = test_http_client_create,
165 };
166
generator_in_test()167 struct flb_aws_client_generator *generator_in_test()
168 {
169 return &test_generator;
170 }
171
172 /* http and ecs providers */
test_http_provider()173 static void test_http_provider()
174 {
175 struct flb_aws_provider *provider;
176 struct flb_aws_credentials *creds;
177 int ret;
178 struct flb_config *config;
179 flb_sds_t host;
180 flb_sds_t path;
181
182 g_request_count = 0;
183
184 config = flb_calloc(1, sizeof(struct flb_config));
185 if (!config) {
186 flb_errno();
187 return;
188 }
189
190 mk_list_init(&config->upstreams);
191
192 host = flb_sds_create("127.0.0.1");
193 if (!host) {
194 flb_errno();
195 return;
196 }
197 path = flb_sds_create("/happy-case");
198 if (!path) {
199 flb_errno();
200 return;
201 }
202
203 provider = flb_http_provider_create(config, host, path,
204 generator_in_test());
205
206 if (!provider) {
207 flb_errno();
208 return;
209 }
210
211 /* repeated calls to get credentials should return the same set */
212 creds = provider->provider_vtable->get_credentials(provider);
213 if (!creds) {
214 flb_errno();
215 return;
216 }
217 TEST_CHECK(strcmp(ACCESS_KEY_HTTP, creds->access_key_id) == 0);
218 TEST_CHECK(strcmp(SECRET_KEY_HTTP, creds->secret_access_key) == 0);
219 TEST_CHECK(strcmp(TOKEN_HTTP, creds->session_token) == 0);
220
221 flb_aws_credentials_destroy(creds);
222
223 creds = provider->provider_vtable->get_credentials(provider);
224 if (!creds) {
225 flb_errno();
226 return;
227 }
228 TEST_CHECK(strcmp(ACCESS_KEY_HTTP, creds->access_key_id) == 0);
229 TEST_CHECK(strcmp(SECRET_KEY_HTTP, creds->secret_access_key) == 0);
230 TEST_CHECK(strcmp(TOKEN_HTTP, creds->session_token) == 0);
231
232 flb_aws_credentials_destroy(creds);
233
234 /* refresh should return 0 (success) */
235 ret = provider->provider_vtable->refresh(provider);
236 TEST_CHECK(ret == 0);
237
238 /*
239 * Request count should be 2:
240 * - One for the first call to get_credentials (2nd should hit cred cache)
241 * - One for the call to refresh
242 */
243 TEST_CHECK(g_request_count == 2);
244
245 flb_aws_provider_destroy(provider);
246 flb_free(config);
247 }
248
test_http_provider_error_case()249 static void test_http_provider_error_case()
250 {
251 struct flb_aws_provider *provider;
252 struct flb_aws_credentials *creds;
253 int ret;
254 struct flb_config *config;
255 flb_sds_t host;
256 flb_sds_t path;
257
258 g_request_count = 0;
259
260 config = flb_calloc(1, sizeof(struct flb_config));
261 if (!config) {
262 flb_errno();
263 return;
264 }
265
266 mk_list_init(&config->upstreams);
267
268 host = flb_sds_create("127.0.0.1");
269 if (!host) {
270 flb_errno();
271 return;
272 }
273 path = flb_sds_create("/error-case");
274 if (!path) {
275 flb_errno();
276 return;
277 }
278
279 provider = flb_http_provider_create(config, host, path,
280 generator_in_test());
281
282 if (!provider) {
283 flb_errno();
284 return;
285 }
286
287 /* get_credentials will fail */
288 creds = provider->provider_vtable->get_credentials(provider);
289 TEST_CHECK(creds == NULL);
290
291 creds = provider->provider_vtable->get_credentials(provider);
292 TEST_CHECK(creds == NULL);
293
294 /* refresh should return -1 (failure) */
295 ret = provider->provider_vtable->refresh(provider);
296 TEST_CHECK(ret < 0);
297
298 /*
299 * Request count should be 3:
300 * - Each call to get_credentials and refresh invokes the client's
301 * request method and returns a request failure.
302 */
303 TEST_CHECK(g_request_count == 3);
304
305 flb_aws_provider_destroy(provider);
306 flb_free(config);
307 }
308
test_http_provider_malformed_response()309 static void test_http_provider_malformed_response()
310 {
311 struct flb_aws_provider *provider;
312 struct flb_aws_credentials *creds;
313 int ret;
314 struct flb_config *config;
315 flb_sds_t host;
316 flb_sds_t path;
317
318 g_request_count = 0;
319
320 config = flb_calloc(1, sizeof(struct flb_config));
321 if (!config) {
322 flb_errno();
323 return;
324 }
325
326 mk_list_init(&config->upstreams);
327
328 host = flb_sds_create("127.0.0.1");
329 if (!host) {
330 flb_errno();
331 return;
332 }
333 path = flb_sds_create("/malformed");
334 if (!path) {
335 flb_errno();
336 return;
337 }
338
339 provider = flb_http_provider_create(config, host, path,
340 generator_in_test());
341
342 if (!provider) {
343 flb_errno();
344 return;
345 }
346
347 /* get_credentials will fail */
348 creds = provider->provider_vtable->get_credentials(provider);
349 TEST_CHECK(creds == NULL);
350
351 creds = provider->provider_vtable->get_credentials(provider);
352 TEST_CHECK(creds == NULL);
353
354 /* refresh should return -1 (failure) */
355 ret = provider->provider_vtable->refresh(provider);
356 TEST_CHECK(ret < 0);
357
358 /*
359 * Request count should be 3:
360 * - Each call to get_credentials and refresh invokes the client's
361 * request method and returns a request failure.
362 */
363 TEST_CHECK(g_request_count == 3);
364
365 flb_aws_provider_destroy(provider);
366 flb_free(config);
367 }
368
369 TEST_LIST = {
370 { "test_http_provider" , test_http_provider},
371 { "test_http_provider_error_case" , test_http_provider_error_case},
372 { "test_http_provider_malformed_response" ,
373 test_http_provider_malformed_response},
374 { 0 }
375 };
376