/src/fluent-bit/plugins/out_chronicle/chronicle_conf.c
Line | Count | Source |
1 | | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* Fluent Bit |
4 | | * ========== |
5 | | * Copyright (C) 2015-2026 The Fluent Bit Authors |
6 | | * |
7 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | | * you may not use this file except in compliance with the License. |
9 | | * You may obtain a copy of the License at |
10 | | * |
11 | | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | | * |
13 | | * Unless required by applicable law or agreed to in writing, software |
14 | | * distributed under the License is distributed on an "AS IS" BASIS, |
15 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | | * See the License for the specific language governing permissions and |
17 | | * limitations under the License. |
18 | | */ |
19 | | |
20 | | #include <fluent-bit/flb_output_plugin.h> |
21 | | #include <fluent-bit/flb_unescape.h> |
22 | | #include <fluent-bit/flb_jsmn.h> |
23 | | #include <fluent-bit/flb_pack.h> |
24 | | #include <fluent-bit/flb_utils.h> |
25 | | #include <fluent-bit/flb_aws_credentials.h> |
26 | | |
27 | | #include <sys/types.h> |
28 | | #include <sys/stat.h> |
29 | | |
30 | | #include "chronicle.h" |
31 | | #include "chronicle_conf.h" |
32 | | |
33 | | |
34 | 0 | static inline int key_cmp(char *str, int len, char *cmp) { |
35 | |
|
36 | 0 | if (strlen(cmp) != len) { |
37 | 0 | return -1; |
38 | 0 | } |
39 | | |
40 | 0 | return strncasecmp(str, cmp, len); |
41 | 0 | } |
42 | | |
43 | | static int flb_chronicle_read_credentials_file(struct flb_chronicle *ctx, |
44 | | char *creds, |
45 | | struct flb_chronicle_oauth_credentials *ctx_creds) |
46 | 0 | { |
47 | 0 | int i; |
48 | 0 | int ret; |
49 | 0 | int len; |
50 | 0 | int key_len; |
51 | 0 | int val_len; |
52 | 0 | int tok_size = 32; |
53 | 0 | char *buf; |
54 | 0 | char *key; |
55 | 0 | char *val; |
56 | 0 | flb_sds_t tmp; |
57 | 0 | struct stat st; |
58 | 0 | jsmn_parser parser; |
59 | 0 | jsmntok_t *t; |
60 | 0 | jsmntok_t *tokens; |
61 | | |
62 | | /* Validate credentials path */ |
63 | 0 | ret = stat(creds, &st); |
64 | 0 | if (ret == -1) { |
65 | 0 | flb_errno(); |
66 | 0 | flb_plg_error(ctx->ins, "cannot open credentials file: %s", |
67 | 0 | creds); |
68 | 0 | return -1; |
69 | 0 | } |
70 | | |
71 | 0 | if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { |
72 | 0 | flb_plg_error(ctx->ins, "credentials file " |
73 | 0 | "is not a valid file: %s", creds); |
74 | 0 | return -1; |
75 | 0 | } |
76 | | |
77 | | /* Read file content */ |
78 | 0 | buf = mk_file_to_buffer(creds); |
79 | 0 | if (!buf) { |
80 | 0 | flb_plg_error(ctx->ins, "error reading credentials file: %s", |
81 | 0 | creds); |
82 | 0 | return -1; |
83 | 0 | } |
84 | | |
85 | | /* Parse content */ |
86 | 0 | jsmn_init(&parser); |
87 | 0 | tokens = flb_calloc(1, sizeof(jsmntok_t) * tok_size); |
88 | 0 | if (!tokens) { |
89 | 0 | flb_errno(); |
90 | 0 | flb_free(buf); |
91 | 0 | return -1; |
92 | 0 | } |
93 | | |
94 | 0 | ret = jsmn_parse(&parser, buf, st.st_size, tokens, tok_size); |
95 | 0 | if (ret <= 0) { |
96 | 0 | flb_plg_error(ctx->ins, "invalid JSON credentials file: %s", |
97 | 0 | creds); |
98 | 0 | flb_free(buf); |
99 | 0 | flb_free(tokens); |
100 | 0 | return -1; |
101 | 0 | } |
102 | | |
103 | 0 | t = &tokens[0]; |
104 | 0 | if (t->type != JSMN_OBJECT) { |
105 | 0 | flb_plg_error(ctx->ins, "invalid JSON map on file: %s", |
106 | 0 | creds); |
107 | 0 | flb_free(buf); |
108 | 0 | flb_free(tokens); |
109 | 0 | return -1; |
110 | 0 | } |
111 | | |
112 | | /* Parse JSON tokens */ |
113 | 0 | for (i = 1; i < ret; i++) { |
114 | 0 | t = &tokens[i]; |
115 | 0 | if (t->type != JSMN_STRING) { |
116 | 0 | continue; |
117 | 0 | } |
118 | | |
119 | 0 | if (t->start == -1 || t->end == -1 || (t->start == 0 && t->end == 0)){ |
120 | 0 | break; |
121 | 0 | } |
122 | | |
123 | | /* Key */ |
124 | 0 | key = buf + t->start; |
125 | 0 | key_len = (t->end - t->start); |
126 | | |
127 | | /* Value */ |
128 | 0 | i++; |
129 | 0 | t = &tokens[i]; |
130 | 0 | val = buf + t->start; |
131 | 0 | val_len = (t->end - t->start); |
132 | |
|
133 | 0 | if (key_cmp(key, key_len, "type") == 0) { |
134 | 0 | ctx_creds->type = flb_sds_create_len(val, val_len); |
135 | 0 | } |
136 | 0 | else if (key_cmp(key, key_len, "project_id") == 0) { |
137 | 0 | ctx_creds->project_id = flb_sds_create_len(val, val_len); |
138 | 0 | } |
139 | 0 | else if (key_cmp(key, key_len, "private_key_id") == 0) { |
140 | 0 | ctx_creds->private_key_id = flb_sds_create_len(val, val_len); |
141 | 0 | } |
142 | 0 | else if (key_cmp(key, key_len, "private_key") == 0) { |
143 | 0 | tmp = flb_sds_create_len(val, val_len); |
144 | 0 | if (tmp) { |
145 | | /* Unescape private key */ |
146 | 0 | len = flb_sds_len(tmp); |
147 | 0 | ctx_creds->private_key = flb_sds_create_size(len); |
148 | 0 | flb_unescape_string(tmp, len, |
149 | 0 | &ctx_creds->private_key); |
150 | 0 | flb_sds_destroy(tmp); |
151 | 0 | } |
152 | 0 | } |
153 | 0 | else if (key_cmp(key, key_len, "client_email") == 0) { |
154 | 0 | ctx_creds->client_email = flb_sds_create_len(val, val_len); |
155 | 0 | } |
156 | 0 | else if (key_cmp(key, key_len, "client_id") == 0) { |
157 | 0 | ctx_creds->client_id = flb_sds_create_len(val, val_len); |
158 | 0 | } |
159 | 0 | else if (key_cmp(key, key_len, "auth_uri") == 0) { |
160 | 0 | ctx_creds->auth_uri = flb_sds_create_len(val, val_len); |
161 | 0 | } |
162 | 0 | else if (key_cmp(key, key_len, "token_uri") == 0) { |
163 | 0 | ctx_creds->token_uri = flb_sds_create_len(val, val_len); |
164 | 0 | } |
165 | 0 | } |
166 | |
|
167 | 0 | flb_free(buf); |
168 | 0 | flb_free(tokens); |
169 | |
|
170 | 0 | return 0; |
171 | 0 | } |
172 | | |
173 | | |
174 | | struct flb_chronicle *flb_chronicle_conf_create(struct flb_output_instance *ins, |
175 | | struct flb_config *config) |
176 | 0 | { |
177 | 0 | int ret; |
178 | 0 | const char *tmp; |
179 | 0 | struct flb_chronicle *ctx; |
180 | 0 | struct flb_chronicle_oauth_credentials *creds; |
181 | | |
182 | | /* Allocate config context */ |
183 | 0 | ctx = flb_calloc(1, sizeof(struct flb_chronicle)); |
184 | 0 | if (!ctx) { |
185 | 0 | flb_errno(); |
186 | 0 | return NULL; |
187 | 0 | } |
188 | 0 | ctx->ins = ins; |
189 | 0 | ctx->config = config; |
190 | |
|
191 | 0 | ret = flb_output_config_map_set(ins, (void *)ctx); |
192 | 0 | if (ret == -1) { |
193 | 0 | flb_plg_error(ins, "unable to load configuration"); |
194 | 0 | flb_free(ctx); |
195 | 0 | return NULL; |
196 | 0 | } |
197 | | |
198 | | /* Lookup credentials file */ |
199 | 0 | creds = flb_calloc(1, sizeof(struct flb_chronicle_oauth_credentials)); |
200 | 0 | if (!creds) { |
201 | 0 | flb_errno(); |
202 | 0 | flb_free(ctx); |
203 | 0 | return NULL; |
204 | 0 | } |
205 | 0 | ctx->oauth_credentials = creds; |
206 | |
|
207 | 0 | if (ctx->credentials_file == NULL) { |
208 | 0 | tmp = getenv("GOOGLE_SERVICE_CREDENTIALS"); |
209 | 0 | if (tmp) { |
210 | 0 | ctx->credentials_file = flb_sds_create(tmp); |
211 | 0 | } |
212 | 0 | } |
213 | |
|
214 | 0 | if (ins->test_mode == FLB_FALSE) { |
215 | 0 | if (ctx->credentials_file) { |
216 | 0 | ret = flb_chronicle_read_credentials_file(ctx, |
217 | 0 | ctx->credentials_file, |
218 | 0 | ctx->oauth_credentials); |
219 | 0 | if (ret != 0) { |
220 | 0 | flb_chronicle_conf_destroy(ctx); |
221 | 0 | return NULL; |
222 | 0 | } |
223 | 0 | } |
224 | 0 | else if (!ctx->credentials_file) { |
225 | | /* |
226 | | * If no credentials file has been defined, do manual lookup of the |
227 | | * client email and the private key. |
228 | | */ |
229 | | |
230 | | /* Service Account Email */ |
231 | 0 | tmp = flb_output_get_property("service_account_email", ins); |
232 | 0 | if (tmp) { |
233 | 0 | creds->client_email = flb_sds_create(tmp); |
234 | 0 | } |
235 | 0 | else { |
236 | 0 | tmp = getenv("SERVICE_ACCOUNT_EMAIL"); |
237 | 0 | if (tmp) { |
238 | 0 | creds->client_email = flb_sds_create(tmp); |
239 | 0 | } |
240 | 0 | } |
241 | | |
242 | | /* Service Account Secret */ |
243 | 0 | tmp = flb_output_get_property("service_account_secret", ins); |
244 | 0 | if (tmp) { |
245 | 0 | creds->private_key = flb_sds_create(tmp); |
246 | 0 | } |
247 | 0 | else { |
248 | 0 | tmp = getenv("SERVICE_ACCOUNT_SECRET"); |
249 | 0 | if (tmp) { |
250 | 0 | creds->private_key = flb_sds_create(tmp); |
251 | 0 | } |
252 | 0 | } |
253 | |
|
254 | 0 | if (!creds->client_email) { |
255 | 0 | flb_plg_error(ctx->ins, "service_account_email/client_email is not defined"); |
256 | 0 | flb_chronicle_conf_destroy(ctx); |
257 | 0 | return NULL; |
258 | 0 | } |
259 | | |
260 | 0 | if (!creds->private_key) { |
261 | 0 | flb_plg_error(ctx->ins, "service_account_secret/private_key is not defined"); |
262 | 0 | flb_chronicle_conf_destroy(ctx); |
263 | 0 | return NULL; |
264 | 0 | } |
265 | 0 | } |
266 | 0 | } |
267 | | |
268 | | /* config: 'project_id' */ |
269 | 0 | if (ctx->project_id == NULL) { |
270 | 0 | if (creds->project_id) { |
271 | | /* flb_config_map_destroy uses the pointer within the config_map struct to |
272 | | * free the value so if we assign it here it is safe to free later with the |
273 | | * creds struct. If we do not we will leak here. |
274 | | */ |
275 | 0 | ctx->project_id = creds->project_id; |
276 | 0 | if (!ctx->project_id) { |
277 | 0 | flb_plg_error(ctx->ins, |
278 | 0 | "failed extracting 'project_id' from credentials."); |
279 | 0 | flb_chronicle_conf_destroy(ctx); |
280 | 0 | return NULL; |
281 | 0 | } |
282 | 0 | } |
283 | 0 | else { |
284 | 0 | flb_plg_error(ctx->ins, |
285 | 0 | "no 'project_id' configured or present in credentials."); |
286 | 0 | flb_chronicle_conf_destroy(ctx); |
287 | 0 | return NULL; |
288 | 0 | } |
289 | 0 | } |
290 | | |
291 | | /* config: 'customer_id' */ |
292 | 0 | if (ctx->customer_id == NULL) { |
293 | 0 | flb_plg_error(ctx->ins, "property 'customer_id' is not defined"); |
294 | 0 | flb_chronicle_conf_destroy(ctx); |
295 | 0 | return NULL; |
296 | 0 | } |
297 | | |
298 | | /* config: 'log_type' */ |
299 | 0 | if (ctx->log_type == NULL) { |
300 | 0 | flb_plg_error(ctx->ins, "property 'log_type' is not defined"); |
301 | 0 | flb_chronicle_conf_destroy(ctx); |
302 | 0 | return NULL; |
303 | 0 | } |
304 | | |
305 | | /* Date key */ |
306 | 0 | ctx->date_key = ctx->json_date_key; |
307 | 0 | tmp = flb_output_get_property("json_date_key", ins); |
308 | 0 | if (tmp) { |
309 | | /* Just check if we have to disable it */ |
310 | 0 | if (flb_utils_bool(tmp) == FLB_FALSE) { |
311 | 0 | ctx->date_key = NULL; |
312 | 0 | } |
313 | 0 | } |
314 | | |
315 | | /* Date format for JSON output */ |
316 | 0 | ctx->json_date_format = FLB_PACK_JSON_DATE_ISO8601; |
317 | 0 | tmp = flb_output_get_property("json_date_format", ins); |
318 | 0 | if (tmp) { |
319 | 0 | ret = flb_pack_to_json_date_type(tmp); |
320 | 0 | if (ret == -1) { |
321 | 0 | flb_plg_error(ctx->ins, "invalid json_date_format '%s'. ", tmp); |
322 | 0 | return NULL; |
323 | 0 | } |
324 | 0 | else { |
325 | 0 | ctx->json_date_format = ret; |
326 | 0 | } |
327 | 0 | } |
328 | | |
329 | | /* Create the target endpoint URI */ |
330 | 0 | ctx->endpoint = flb_sds_create_size(sizeof(FLB_CHRONICLE_UNSTRUCTURED_ENDPOINT)); |
331 | 0 | if (!ctx->endpoint) { |
332 | 0 | flb_errno(); |
333 | 0 | flb_chronicle_conf_destroy(ctx); |
334 | 0 | return NULL; |
335 | 0 | } |
336 | 0 | ctx->endpoint = flb_sds_printf(&ctx->endpoint, FLB_CHRONICLE_UNSTRUCTURED_ENDPOINT); |
337 | | |
338 | | /* Create the base URI */ |
339 | 0 | if (ctx->region == NULL || strncasecmp(ctx->region, "US", 2) == 0) { |
340 | 0 | ctx->uri = flb_sds_create_size(sizeof(FLB_CHRONICLE_URL_BASE)); |
341 | 0 | if (!ctx->uri) { |
342 | 0 | flb_errno(); |
343 | 0 | flb_chronicle_conf_destroy(ctx); |
344 | 0 | return NULL; |
345 | 0 | } |
346 | 0 | ctx->uri = flb_sds_printf(&ctx->uri, FLB_CHRONICLE_URL_BASE); |
347 | 0 | } |
348 | 0 | else if (strncasecmp(ctx->region, "EU", 2) == 0){ |
349 | 0 | ctx->uri = flb_sds_create_size(sizeof(FLB_CHRONICLE_URL_BASE_EU)); |
350 | 0 | if (!ctx->uri) { |
351 | 0 | flb_errno(); |
352 | 0 | flb_chronicle_conf_destroy(ctx); |
353 | 0 | return NULL; |
354 | 0 | } |
355 | 0 | ctx->uri = flb_sds_printf(&ctx->uri, FLB_CHRONICLE_URL_BASE_EU); |
356 | 0 | } |
357 | 0 | else if (strncasecmp(ctx->region, "UK", 2) == 0) { |
358 | 0 | ctx->uri = flb_sds_create_size(sizeof(FLB_CHRONICLE_URL_BASE_UK)); |
359 | 0 | if (!ctx->uri) { |
360 | 0 | flb_errno(); |
361 | 0 | flb_chronicle_conf_destroy(ctx); |
362 | 0 | return NULL; |
363 | 0 | } |
364 | 0 | ctx->uri = flb_sds_printf(&ctx->uri, FLB_CHRONICLE_URL_BASE_UK); |
365 | 0 | } |
366 | 0 | else if (strncasecmp(ctx->region, "ASIA", 4) == 0) { |
367 | 0 | ctx->uri = flb_sds_create_size(sizeof(FLB_CHRONICLE_URL_BASE_ASIA)); |
368 | 0 | if (!ctx->uri) { |
369 | 0 | flb_errno(); |
370 | 0 | flb_chronicle_conf_destroy(ctx); |
371 | 0 | return NULL; |
372 | 0 | } |
373 | 0 | ctx->uri = flb_sds_printf(&ctx->uri, FLB_CHRONICLE_URL_BASE_ASIA); |
374 | 0 | } |
375 | 0 | else { |
376 | 0 | flb_plg_error(ctx->ins, "unsupported region"); |
377 | 0 | flb_chronicle_conf_destroy(ctx); |
378 | 0 | return NULL; |
379 | 0 | } |
380 | 0 | flb_plg_info(ctx->ins, "project='%s' custumer_id='%s' region='%s'", |
381 | 0 | ctx->project_id, ctx->customer_id, ctx->region); |
382 | |
|
383 | 0 | return ctx; |
384 | 0 | } |
385 | | |
386 | | |
387 | | int flb_chronicle_oauth_credentials_destroy(struct flb_chronicle_oauth_credentials *creds) |
388 | 0 | { |
389 | 0 | if (!creds) { |
390 | 0 | return -1; |
391 | 0 | } |
392 | 0 | flb_sds_destroy(creds->type); |
393 | 0 | flb_sds_destroy(creds->project_id); |
394 | 0 | flb_sds_destroy(creds->private_key_id); |
395 | 0 | flb_sds_destroy(creds->private_key); |
396 | 0 | flb_sds_destroy(creds->client_email); |
397 | 0 | flb_sds_destroy(creds->client_id); |
398 | 0 | flb_sds_destroy(creds->auth_uri); |
399 | 0 | flb_sds_destroy(creds->token_uri); |
400 | |
|
401 | 0 | flb_free(creds); |
402 | |
|
403 | 0 | return 0; |
404 | 0 | } |
405 | | |
406 | | int flb_chronicle_conf_destroy(struct flb_chronicle *ctx) |
407 | 0 | { |
408 | 0 | if (!ctx) { |
409 | 0 | return -1; |
410 | 0 | } |
411 | | |
412 | 0 | flb_chronicle_oauth_credentials_destroy(ctx->oauth_credentials); |
413 | |
|
414 | 0 | flb_sds_destroy(ctx->endpoint); |
415 | 0 | flb_sds_destroy(ctx->uri); |
416 | |
|
417 | 0 | if (ctx->o) { |
418 | 0 | flb_oauth2_destroy(ctx->o); |
419 | 0 | } |
420 | |
|
421 | 0 | flb_free(ctx); |
422 | 0 | return 0; |
423 | 0 | } |