Coverage Report

Created: 2026-03-22 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}