Coverage Report

Created: 2026-11-12 07:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fluent-bit/plugins/out_azure_blob/azure_blob.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_sds.h>
22
#include <fluent-bit/flb_kv.h>
23
#include <fluent-bit/flb_utils.h>
24
#include <fluent-bit/flb_pack.h>
25
#include <fluent-bit/flb_config_map.h>
26
#include <fluent-bit/flb_gzip.h>
27
#include <fluent-bit/flb_compression.h>
28
#include <fluent-bit/flb_zstd.h>
29
#include <fluent-bit/flb_base64.h>
30
#include <fluent-bit/flb_sqldb.h>
31
#include <fluent-bit/flb_input_blob.h>
32
#include <fluent-bit/flb_log_event_decoder.h>
33
#include <fluent-bit/flb_plugin.h>
34
#include <fluent-bit/flb_notification.h>
35
#include <fluent-bit/flb_scheduler.h>
36
#include <fluent-bit/flb_record_accessor.h>
37
#include <fluent-bit/flb_random.h>
38
39
#include <msgpack.h>
40
#include <string.h>
41
42
#include "azure_blob.h"
43
#include "azure_blob_db.h"
44
#include "azure_blob_uri.h"
45
#include "azure_blob_conf.h"
46
#include "azure_blob_appendblob.h"
47
#include "azure_blob_blockblob.h"
48
#include "azure_blob_http.h"
49
#include "azure_blob_store.h"
50
51
0
#define CREATE_BLOB  1337
52
0
#define AZB_UUID_PLACEHOLDER "$UUID"
53
54
/* thread_local_storage for workers */
55
56
struct worker_info {
57
    int active_upload;
58
};
59
60
FLB_TLS_DEFINE(struct worker_info, worker_info);
61
62
static int create_blob(struct flb_azure_blob *ctx, const char *path_prefix, char *name);
63
64
static int azure_blob_format(struct flb_config *config,
65
                             struct flb_input_instance *ins,
66
                             void *plugin_context,
67
                             void *flush_ctx,
68
                             int event_type,
69
                             const char *tag, int tag_len,
70
                             const void *data, size_t bytes,
71
                             void **out_data, size_t *out_size)
72
0
{
73
0
    flb_sds_t out_buf;
74
0
    struct flb_azure_blob *ctx = plugin_context;
75
76
0
    out_buf = flb_pack_msgpack_to_json_format(data, bytes,
77
0
                                              FLB_PACK_JSON_FORMAT_LINES,
78
0
                                              FLB_PACK_JSON_DATE_ISO8601,
79
0
                                              ctx->date_key,
80
0
                                              config->json_escape_unicode);
81
0
    if (!out_buf) {
82
0
        return -1;
83
0
    }
84
85
0
    *out_data = out_buf;
86
0
    *out_size = flb_sds_len(out_buf);
87
0
    return 0;
88
0
}
89
90
/*
91
 * Either new_data or chunk can be NULL, but not both
92
 */
93
static int construct_request_buffer(struct flb_azure_blob *ctx, flb_sds_t new_data,
94
                                    struct azure_blob_file *upload_file,
95
                                    char **out_buf, size_t *out_size)
96
0
{
97
0
    char *body;
98
0
    char *tmp;
99
0
    size_t body_size = 0;
100
0
    char *buffered_data = NULL;
101
0
    size_t buffer_size = 0;
102
0
    int ret;
103
104
0
    if (new_data == NULL && upload_file == NULL) {
105
0
        flb_plg_error(ctx->ins, "[construct_request_buffer] Something went wrong"
106
0
                                " both chunk and new_data are NULL");
107
0
        return -1;
108
0
    }
109
110
0
    if (upload_file) {
111
0
        ret = azure_blob_store_file_upload_read(ctx, upload_file->fsf, &buffered_data, &buffer_size);
112
0
        if (ret < 0) {
113
0
            flb_plg_error(ctx->ins, "Could not read locally buffered data %s",
114
0
                          upload_file->fsf->name);
115
0
            return -1;
116
0
        }
117
118
        /*
119
         * lock the upload_file from buffer list
120
         */
121
0
        azure_blob_store_file_lock(upload_file);
122
0
        body = buffered_data;
123
0
        body_size = buffer_size;
124
0
    }
125
126
0
    flb_plg_debug(ctx->ins, "[construct_request_buffer] size of buffer file read %zu", buffer_size);
127
128
    /*
129
     * If new data is arriving, increase the original 'buffered_data' size
130
     * to append the new one.
131
     */
132
0
    if (new_data) {
133
0
        body_size += flb_sds_len(new_data);
134
0
        flb_plg_debug(ctx->ins, "[construct_request_buffer] size of new_data %zu", body_size);
135
136
0
        tmp = flb_realloc(buffered_data, body_size + 1);
137
0
        if (!tmp) {
138
0
            flb_errno();
139
0
            flb_free(buffered_data);
140
0
            if (upload_file) {
141
0
                azure_blob_store_file_unlock(upload_file);
142
0
            }
143
0
            return -1;
144
0
        }
145
0
        body = buffered_data = tmp;
146
0
        memcpy(body + buffer_size, new_data, flb_sds_len(new_data));
147
0
        if (ctx->compression == FLB_COMPRESSION_ALGORITHM_NONE) {
148
0
            body[body_size] = '\0';
149
0
        }
150
0
    }
151
152
0
    flb_plg_debug(ctx->ins, "[construct_request_buffer] final increased %zu", body_size);
153
154
0
    *out_buf = body;
155
0
    *out_size = body_size;
156
157
0
    return 0;
158
0
}
159
160
/*
161
 * Compress a payload using the configured algorithm. Returns 0 on success and
162
 * negative on failure so callers can gracefully fall back to sending the raw
163
 * payload.
164
 */
165
static int azure_blob_compress_payload(int algorithm,
166
                                       void *in_data, size_t in_len,
167
                                       void **out_data, size_t *out_len)
168
0
{
169
0
    if (algorithm == FLB_COMPRESSION_ALGORITHM_GZIP) {
170
0
        return flb_gzip_compress(in_data, in_len, out_data, out_len);
171
0
    }
172
0
    else if (algorithm == FLB_COMPRESSION_ALGORITHM_ZSTD) {
173
0
        return flb_zstd_compress(in_data, in_len, out_data, out_len);
174
0
    }
175
176
0
    return -1;
177
0
}
178
179
/* Map a compression algorithm to its human-friendly label for logs. */
180
static const char *azure_blob_compression_name(int algorithm)
181
0
{
182
0
    if (algorithm == FLB_COMPRESSION_ALGORITHM_GZIP) {
183
0
        return "gzip";
184
0
    }
185
0
    else if (algorithm == FLB_COMPRESSION_ALGORITHM_ZSTD) {
186
0
        return "zstd";
187
0
    }
188
189
0
    return "unknown";
190
0
}
191
192
/**
193
 * Populate the provided buffer with pseudo-random alphanumeric characters.
194
 * The buffer must have room for `length + 1` bytes to include the terminator.
195
 */
196
void generate_random_string_blob(char *str, size_t length)
197
0
{
198
0
    const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
199
0
    const size_t charset_size = sizeof(charset) - 1;
200
0
    unsigned char *rand_buf;
201
0
    size_t i;
202
203
0
    if (length == 0) {
204
0
        str[0] = '\0';
205
0
        return;
206
0
    }
207
208
0
    rand_buf = flb_malloc(length);
209
0
    if (!rand_buf) {
210
0
        flb_errno();
211
0
        memset(str, 'a', length);
212
0
        str[length] = '\0';
213
0
        return;
214
0
    }
215
216
0
    if (flb_random_bytes(rand_buf, length) != 0) {
217
0
        struct flb_time now;
218
0
        uint32_t state;
219
220
0
        flb_time_get(&now);
221
0
        state = (uint32_t) now.tm.tv_nsec ^ (uint32_t) now.tm.tv_sec;
222
0
        for (i = 0; i < length; i++) {
223
0
            state = state * 1103515245u + 12345u;
224
0
            rand_buf[i] = (unsigned char) (state >> 16);
225
0
        }
226
0
    }
227
228
0
    for (i = 0; i < length; i++) {
229
0
        str[i] = charset[rand_buf[i] % charset_size];
230
0
    }
231
0
    str[length] = '\0';
232
233
0
    flb_free(rand_buf);
234
0
}
235
236
static inline int azb_path_uses_templating_tokens(const char *path)
237
0
{
238
0
    size_t i;
239
240
0
    if (!path) {
241
0
        return FLB_FALSE;
242
0
    }
243
244
0
    for (i = 0; path[i] != '\0'; i++) {
245
0
        if (path[i] == '$' || path[i] == '%') {
246
0
            return FLB_TRUE;
247
0
        }
248
0
    }
249
250
0
    return FLB_FALSE;
251
0
}
252
253
const char *azb_commit_prefix_with_fallback(struct flb_azure_blob *ctx,
254
                                            const char *db_prefix)
255
0
{
256
0
    if (db_prefix) {
257
0
        return db_prefix;
258
0
    }
259
260
0
    if (!ctx || !ctx->path) {
261
0
        return NULL;
262
0
    }
263
264
0
    if (ctx->path_templating_enabled == FLB_FALSE ||
265
0
        azb_path_uses_templating_tokens(ctx->path) == FLB_FALSE) {
266
0
        return ctx->path;
267
0
    }
268
269
0
    return NULL;
270
0
}
271
272
/**
273
 * Replace all "$UUID" placeholders in the path with the same random suffix.
274
 * Returns a newly allocated SDS string and frees the original `path` value.
275
 */
276
static flb_sds_t azb_replace_uuid(flb_sds_t path)
277
0
{
278
0
    char random_buf[9] = {0};
279
0
    const size_t token_len = strlen(AZB_UUID_PLACEHOLDER);
280
0
    size_t occurrences = 0;
281
0
    size_t path_len;
282
0
    size_t result_len;
283
0
    char *cursor;
284
0
    char *match;
285
0
    char *dst;
286
0
    flb_sds_t result;
287
288
0
    if (!path) {
289
0
        return NULL;
290
0
    }
291
292
0
    cursor = path;
293
0
    while ((match = strstr(cursor, AZB_UUID_PLACEHOLDER)) != NULL) {
294
0
        occurrences++;
295
0
        cursor = match + token_len;
296
0
    }
297
298
0
    if (occurrences == 0) {
299
0
        return path;
300
0
    }
301
302
0
    generate_random_string_blob(random_buf, 8);
303
304
0
    path_len = flb_sds_len(path);
305
0
    result_len = path_len + occurrences * (8 - token_len);
306
307
0
    result = flb_sds_create_size(result_len + 1);
308
0
    if (!result) {
309
0
        flb_errno();
310
0
        flb_sds_destroy(path);
311
0
        return NULL;
312
0
    }
313
314
0
    dst = result;
315
0
    cursor = path;
316
0
    while ((match = strstr(cursor, AZB_UUID_PLACEHOLDER)) != NULL) {
317
0
        size_t segment_len;
318
319
0
        segment_len = (size_t)(match - cursor);
320
0
        if (segment_len > 0) {
321
0
            memcpy(dst, cursor, segment_len);
322
0
            dst += segment_len;
323
0
        }
324
325
0
        memcpy(dst, random_buf, 8);
326
0
        dst += 8;
327
328
0
        cursor = match + token_len;
329
0
    }
330
331
0
    if (cursor < path + path_len) {
332
0
        size_t tail_len;
333
334
0
        tail_len = (size_t)((path + path_len) - cursor);
335
0
        if (tail_len > 0) {
336
0
            memcpy(dst, cursor, tail_len);
337
0
            dst += tail_len;
338
0
        }
339
0
    }
340
341
0
    *dst = '\0';
342
0
    flb_sds_len_set(result, result_len);
343
344
0
    flb_sds_destroy(path);
345
0
    return result;
346
0
}
347
348
/**
349
 * Replace the first occurrence of `token` with `replacement` in the SDS input.
350
 * The original string is destroyed and a new SDS instance is returned.
351
 */
352
static flb_sds_t azb_simple_replace(flb_sds_t input,
353
                                    const char *token,
354
                                    const char *replacement)
355
0
{
356
0
    char *pos;
357
0
    size_t token_len;
358
0
    size_t replace_len;
359
0
    size_t prefix_len;
360
0
    size_t suffix_len;
361
0
    flb_sds_t result;
362
363
0
    if (!input || !token) {
364
0
        return input;
365
0
    }
366
367
0
    pos = strstr(input, token);
368
0
    if (!pos) {
369
0
        return input;
370
0
    }
371
372
0
    token_len = strlen(token);
373
0
    replace_len = strlen(replacement);
374
0
    prefix_len = (size_t)(pos - input);
375
0
    suffix_len = flb_sds_len(input) - prefix_len - token_len;
376
377
0
    result = flb_sds_create_size(prefix_len + replace_len + suffix_len + 1);
378
0
    if (!result) {
379
0
        flb_errno();
380
0
        flb_sds_destroy(input);
381
0
        return NULL;
382
0
    }
383
384
0
    if (prefix_len > 0) {
385
0
        memcpy(result, input, prefix_len);
386
0
    }
387
0
    if (replace_len > 0) {
388
0
        memcpy(result + prefix_len, replacement, replace_len);
389
0
    }
390
0
    if (suffix_len > 0) {
391
0
        memcpy(result + prefix_len + replace_len, pos + token_len, suffix_len);
392
0
    }
393
0
    result[prefix_len + replace_len + suffix_len] = '\0';
394
0
    flb_sds_len_set(result, prefix_len + replace_len + suffix_len);
395
396
0
    flb_sds_destroy(input);
397
0
    return result;
398
0
}
399
400
/**
401
 * Expand millisecond and nanosecond custom tokens within the blob path.
402
 */
403
static flb_sds_t azb_apply_time_tokens(flb_sds_t path, const struct flb_time *timestamp)
404
0
{
405
0
    char ms_buf[4];
406
0
    char ns_buf[10];
407
0
    flb_sds_t tmp;
408
409
0
    if (!path || !timestamp) {
410
0
        return path;
411
0
    }
412
413
0
    snprintf(ms_buf, sizeof(ms_buf), "%03lu",
414
0
             (unsigned long)(timestamp->tm.tv_nsec / 1000000));
415
0
    snprintf(ns_buf, sizeof(ns_buf), "%09lu",
416
0
             (unsigned long)timestamp->tm.tv_nsec);
417
418
    /* Replace %3N with milliseconds */
419
0
    tmp = azb_simple_replace(path, "%3N", ms_buf);
420
0
    if (!tmp) {
421
0
        return NULL;
422
0
    }
423
0
    path = tmp;
424
425
    /* Replace %9N with nanoseconds */
426
0
    tmp = azb_simple_replace(path, "%9N", ns_buf);
427
0
    if (!tmp) {
428
0
        return NULL;
429
0
    }
430
0
    path = tmp;
431
432
    /* Replace %L with nanoseconds */
433
0
    tmp = azb_simple_replace(path, "%L", ns_buf);
434
0
    if (!tmp) {
435
0
        return NULL;
436
0
    }
437
438
0
    return tmp;
439
0
}
440
441
/**
442
 * Apply `strftime` formatting using the provided event timestamp.
443
 */
444
static flb_sds_t azb_apply_strftime(struct flb_azure_blob *ctx,
445
                                    flb_sds_t path,
446
                                    const struct flb_time *timestamp)
447
0
{
448
0
    struct flb_time now;
449
0
    const struct flb_time *ref;
450
0
    struct tm tm_utc;
451
0
    time_t seconds;
452
0
    size_t path_len;
453
0
    size_t empty_threshold;
454
0
    size_t buf_size;
455
0
    size_t out_len;
456
0
    char *buf;
457
0
    char *tmp_buf;
458
0
    flb_sds_t result;
459
460
0
    if (!path) {
461
0
        return NULL;
462
0
    }
463
464
0
    if (timestamp) {
465
0
        ref = timestamp;
466
0
    }
467
0
    else {
468
0
        flb_time_get(&now);
469
0
        ref = &now;
470
0
    }
471
472
0
    seconds = ref->tm.tv_sec;
473
0
    if (!gmtime_r(&seconds, &tm_utc)) {
474
0
        flb_sds_destroy(path);
475
0
        return NULL;
476
0
    }
477
478
0
    path_len = flb_sds_len(path);
479
0
    empty_threshold = path_len > 0 ? path_len * 2 : 2;
480
0
    buf_size = path_len + 64;
481
0
    buf = flb_malloc(buf_size + 1);
482
0
    if (!buf) {
483
0
        flb_errno();
484
0
        flb_sds_destroy(path);
485
0
        return NULL;
486
0
    }
487
488
0
    buf[0] = '\0';
489
490
0
    while (1) {
491
0
        out_len = strftime(buf, buf_size + 1, path, &tm_utc);
492
0
        if (out_len > 0) {
493
0
            break;
494
0
        }
495
496
0
        if (buf_size > empty_threshold) {
497
0
            break;
498
0
        }
499
500
0
        if (buf_size > 4096) {
501
0
            break;
502
0
        }
503
504
0
        buf_size *= 2;
505
0
        tmp_buf = flb_realloc(buf, buf_size + 1);
506
0
        if (!tmp_buf) {
507
0
            flb_errno();
508
0
            flb_free(buf);
509
0
            flb_sds_destroy(path);
510
0
            return NULL;
511
0
        }
512
0
        buf = tmp_buf;
513
0
    }
514
515
0
    if (out_len == 0) {
516
0
        if (ctx && ctx->ins) {
517
0
            flb_plg_error(ctx->ins,
518
0
                          "[azure_blob] invalid or too-long strftime path template");
519
0
        }
520
0
        else {
521
0
            flb_error("[azure_blob] invalid or too-long strftime path template");
522
0
        }
523
0
        flb_free(buf);
524
0
        flb_sds_destroy(path);
525
0
        return NULL;
526
0
    }
527
528
0
    result = flb_sds_create_len(buf, out_len);
529
0
    if (!result) {
530
0
        flb_errno();
531
0
        flb_free(buf);
532
0
        flb_sds_destroy(path);
533
0
        return NULL;
534
0
    }
535
536
0
    flb_free(buf);
537
0
    flb_sds_destroy(path);
538
539
0
    return result;
540
0
}
541
542
/**
543
 * Remove leading and trailing slashes to avoid double separators in URIs.
544
 */
545
static void azb_trim_slashes(flb_sds_t path)
546
0
{
547
0
    size_t len;
548
0
    size_t start = 0;
549
0
    char *buf;
550
551
0
    if (!path) {
552
0
        return;
553
0
    }
554
555
0
    buf = path;
556
0
    len = flb_sds_len(path);
557
558
0
    while (start < len && buf[start] == '/') {
559
0
        start++;
560
0
    }
561
562
0
    if (start > 0) {
563
0
        memmove(buf, buf + start, len - start + 1);
564
0
        len -= start;
565
0
        flb_sds_len_set(path, len);
566
0
    }
567
568
0
    while (len > 0 && buf[len - 1] == '/') {
569
0
        len--;
570
0
    }
571
0
    buf[len] = '\0';
572
0
    flb_sds_len_set(path, len);
573
0
}
574
575
/**
576
 * Build the final blob path by applying record accessors and time templating.
577
 */
578
int azb_resolve_path(struct flb_azure_blob *ctx,
579
                     const char *tag,
580
                     int tag_len,
581
                     const struct flb_time *timestamp,
582
                     flb_sds_t *out_path)
583
0
{
584
0
    flb_sds_t path;
585
0
    struct flb_time now;
586
0
    msgpack_sbuffer sbuf;
587
0
    msgpack_packer pk;
588
0
    msgpack_unpacked result;
589
0
    msgpack_object root;
590
0
    struct flb_record_accessor *temp_ra;
591
0
    flb_sds_t expanded;
592
593
0
    if (!out_path) {
594
0
        return -1;
595
0
    }
596
597
0
    *out_path = NULL;
598
599
0
    if (!ctx->path_templating_enabled) {
600
0
        return 0;
601
0
    }
602
603
0
    if (!ctx->path) {
604
        /* No template to apply; behave as if templating was disabled */
605
0
        return 0;
606
0
    }
607
608
0
    if (!timestamp) {
609
0
        flb_time_get(&now);
610
0
        timestamp = &now;
611
0
    }
612
613
    /* Start with the original path template */
614
0
    path = flb_sds_create_len(ctx->path, flb_sds_len(ctx->path));
615
0
    if (!path) {
616
0
        flb_errno();
617
0
        return -1;
618
0
    }
619
620
    /* Apply UUID replacement before record accessor step.
621
     * Unknown $ tokens get stripped otherwise.
622
     */
623
0
    path = azb_replace_uuid(path);
624
0
    if (!path) {
625
0
        return -1;
626
0
    }
627
628
    /* Apply time tokens (%3N, %9N, %L) */
629
0
    path = azb_apply_time_tokens(path, timestamp);
630
0
    if (!path) {
631
0
        return -1;
632
0
    }
633
634
    /* Apply strftime */
635
0
    path = azb_apply_strftime(ctx, path, timestamp);
636
0
    if (!path) {
637
0
        return -1;
638
0
    }
639
640
    /* Now use record accessor to expand $TAG and $TAG[n] */
641
    /* Create empty msgpack map for record accessor */
642
0
    msgpack_sbuffer_init(&sbuf);
643
0
    msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
644
0
    msgpack_pack_map(&pk, 0);
645
646
    /* Unpack to get msgpack_object */
647
0
    msgpack_unpacked_init(&result);
648
0
    if (msgpack_unpack_next(&result,
649
0
                            sbuf.data,
650
0
                            sbuf.size,
651
0
                            NULL) != MSGPACK_UNPACK_SUCCESS) {
652
0
        msgpack_sbuffer_destroy(&sbuf);
653
0
        msgpack_unpacked_destroy(&result);
654
0
        flb_sds_destroy(path);
655
0
        return -1;
656
0
    }
657
0
    root = result.data;
658
659
    /* Create a temporary record accessor for the partially-processed path */
660
0
    temp_ra = flb_ra_create(path, FLB_TRUE);
661
0
    if (!temp_ra) {
662
0
        msgpack_unpacked_destroy(&result);
663
0
        msgpack_sbuffer_destroy(&sbuf);
664
0
        flb_sds_destroy(path);
665
0
        return -1;
666
0
    }
667
668
    /* Use record accessor to expand $TAG and $TAG[n] */
669
0
    {
670
0
        const char *ra_tag = tag;
671
0
        int ra_tag_len = tag_len;
672
673
0
        if (!ra_tag) {
674
0
            ra_tag = "";
675
0
            ra_tag_len = 0;
676
0
        }
677
678
0
        expanded = flb_ra_translate(temp_ra, (char *) ra_tag, ra_tag_len, root, NULL);
679
0
    }
680
681
0
    flb_ra_destroy(temp_ra);
682
0
    msgpack_unpacked_destroy(&result);
683
0
    msgpack_sbuffer_destroy(&sbuf);
684
0
    flb_sds_destroy(path);
685
686
0
    if (!expanded) {
687
0
        return -1;
688
0
    }
689
690
0
    azb_trim_slashes(expanded);
691
692
0
    if (flb_sds_len(expanded) == 0) {
693
0
        *out_path = expanded;
694
0
        return 0;
695
0
    }
696
697
0
    *out_path = expanded;
698
0
    return 0;
699
0
}
700
701
static int create_blob(struct flb_azure_blob *ctx, const char *path_prefix, char *name)
702
0
{
703
0
    int ret;
704
0
    int status = FLB_OK;
705
0
    size_t b_sent;
706
0
    flb_sds_t uri = NULL;
707
0
    struct flb_http_client *c;
708
0
    struct flb_connection *u_conn;
709
710
0
    uri = azb_uri_create_blob(ctx, path_prefix, name);
711
0
    if (!uri) {
712
0
        return FLB_RETRY;
713
0
    }
714
715
    /* Get upstream connection */
716
0
    u_conn = flb_upstream_conn_get(ctx->u);
717
0
    if (!u_conn) {
718
0
        flb_plg_error(ctx->ins,
719
0
                      "cannot create upstream connection for create_append_blob");
720
0
        flb_sds_destroy(uri);
721
0
        return FLB_RETRY;
722
0
    }
723
724
    /* Create HTTP client context */
725
0
    c = flb_http_client(u_conn, FLB_HTTP_PUT,
726
0
                        uri,
727
0
                        NULL, 0, NULL, 0, NULL, 0);
728
0
    if (!c) {
729
0
        flb_plg_error(ctx->ins, "cannot create HTTP client context");
730
0
        flb_upstream_conn_release(u_conn);
731
0
        flb_sds_destroy(uri);
732
0
        return FLB_RETRY;
733
0
    }
734
735
    /* Prepare headers and authentication */
736
0
    azb_http_client_setup(ctx, c, -1, FLB_TRUE,
737
0
                          AZURE_BLOB_CT_NONE, AZURE_BLOB_CE_NONE);
738
739
    /* Send HTTP request */
740
0
    ret = flb_http_do(c, &b_sent);
741
742
0
    if (ret == -1) {
743
0
        flb_plg_error(ctx->ins, "error sending append_blob");
744
0
        status = FLB_RETRY;
745
0
        goto cleanup_create;
746
0
    }
747
748
0
    if (c->resp.status == 201) {
749
        /* delete "&sig=..." in the c->uri for security */
750
0
        char *p = strstr(c->uri, "&sig=");
751
0
        if (p) {
752
0
            *p = '\0';
753
0
        }
754
0
        flb_plg_info(ctx->ins, "blob created successfully: %s", c->uri);
755
0
    }
756
0
    else {
757
0
        if (c->resp.payload_size > 0) {
758
0
            flb_plg_error(ctx->ins, "http_status=%i cannot create append blob\n%s",
759
0
                          c->resp.status, c->resp.payload);
760
0
        }
761
0
        else {
762
0
            flb_plg_error(ctx->ins, "http_status=%i cannot create append blob",
763
0
                          c->resp.status);
764
0
        }
765
0
        status = FLB_RETRY;
766
0
        goto cleanup_create;
767
0
    }
768
769
0
cleanup_create:
770
0
    flb_http_client_destroy(c);
771
0
    flb_upstream_conn_release(u_conn);
772
0
    if (uri) {
773
0
        flb_sds_destroy(uri);
774
0
    }
775
0
    return status;
776
0
}
777
778
static int delete_blob(struct flb_azure_blob *ctx,
779
                       const char *path_prefix,
780
                       char *name)
781
0
{
782
0
    int ret;
783
0
    int status = FLB_OK;
784
0
    size_t b_sent;
785
0
    flb_sds_t uri = NULL;
786
0
    struct flb_http_client *c;
787
0
    struct flb_connection *u_conn;
788
789
0
    uri = azb_uri_create_blob(ctx, path_prefix, name);
790
0
    if (!uri) {
791
0
        return FLB_RETRY;
792
0
    }
793
794
    /* Get upstream connection */
795
0
    u_conn = flb_upstream_conn_get(ctx->u);
796
0
    if (!u_conn) {
797
0
        flb_plg_error(ctx->ins,
798
0
                      "cannot create upstream connection for create_append_blob");
799
0
        flb_sds_destroy(uri);
800
0
        return FLB_RETRY;
801
0
    }
802
803
    /* Create HTTP client context */
804
0
    c = flb_http_client(u_conn, FLB_HTTP_DELETE,
805
0
                        uri,
806
0
                        NULL, 0, NULL, 0, NULL, 0);
807
0
    if (!c) {
808
0
        flb_plg_error(ctx->ins, "cannot create HTTP client context");
809
0
        flb_upstream_conn_release(u_conn);
810
0
        flb_sds_destroy(uri);
811
0
        return FLB_RETRY;
812
0
    }
813
814
    /* Prepare headers and authentication */
815
0
    azb_http_client_setup(ctx, c, -1, FLB_TRUE,
816
0
                          AZURE_BLOB_CT_NONE, AZURE_BLOB_CE_NONE);
817
818
    /* Send HTTP request */
819
0
    ret = flb_http_do(c, &b_sent);
820
821
0
    if (ret == -1) {
822
0
        flb_plg_error(ctx->ins, "error sending append_blob");
823
0
        status = FLB_RETRY;
824
0
        goto cleanup_delete;
825
0
    }
826
827
0
    if (c->resp.status == 202) {
828
        /* delete "&sig=..." in the c->uri for security */
829
0
        char *p = strstr(c->uri, "&sig=");
830
0
        if (p) {
831
0
            *p = '\0';
832
0
        }
833
0
        flb_plg_info(ctx->ins, "blob deleted successfully: %s", c->uri);
834
0
    }
835
0
    else {
836
0
        if (c->resp.payload_size > 0) {
837
0
            flb_plg_error(ctx->ins, "http_status=%i cannot delete append blob\n%s",
838
0
                          c->resp.status, c->resp.payload);
839
0
        }
840
0
        else {
841
0
            flb_plg_error(ctx->ins, "http_status=%i cannot delete append blob",
842
0
                          c->resp.status);
843
0
        }
844
0
        status = FLB_RETRY;
845
0
        goto cleanup_delete;
846
0
    }
847
848
0
cleanup_delete:
849
0
    flb_http_client_destroy(c);
850
0
    flb_upstream_conn_release(u_conn);
851
0
    if (uri) {
852
0
        flb_sds_destroy(uri);
853
0
    }
854
0
    return status;
855
0
}
856
857
int azb_select_compression_strategy(struct flb_azure_blob *ctx,
858
                                    int *compression_algorithm,
859
                                    int *prefer_blob_compression)
860
0
{
861
0
    int algorithm = FLB_COMPRESSION_ALGORITHM_NONE;
862
0
    int prefer_blob = FLB_FALSE;
863
864
0
    if (ctx->compress_blob == FLB_TRUE) {
865
0
        prefer_blob = FLB_TRUE;
866
0
        if (ctx->compression != FLB_COMPRESSION_ALGORITHM_NONE) {
867
0
            algorithm = ctx->compression;
868
0
        }
869
0
        else {
870
0
            algorithm = FLB_COMPRESSION_ALGORITHM_GZIP;
871
0
        }
872
0
    }
873
0
    else if (ctx->compression != FLB_COMPRESSION_ALGORITHM_NONE) {
874
0
        algorithm = ctx->compression;
875
0
    }
876
877
0
    if (compression_algorithm != NULL) {
878
0
        *compression_algorithm = algorithm;
879
0
    }
880
881
0
    if (prefer_blob_compression != NULL) {
882
0
        *prefer_blob_compression = prefer_blob;
883
0
    }
884
885
0
    return 0;
886
0
}
887
888
static int http_send_blob(struct flb_config *config, struct flb_azure_blob *ctx,
889
                          flb_sds_t ref_name,
890
                          flb_sds_t uri,
891
                          flb_sds_t block_id,
892
                          int event_type,
893
                          void *data, size_t bytes)
894
0
{
895
0
    int ret;
896
0
    int compressed = FLB_FALSE;
897
0
    int content_encoding = AZURE_BLOB_CE_NONE;
898
0
    int content_type = AZURE_BLOB_CT_NONE;
899
0
    int compression_algorithm = FLB_COMPRESSION_ALGORITHM_NONE;
900
0
    int network_compression_algorithm = ctx->compression;
901
0
    int network_compression_applied = FLB_FALSE;
902
0
    int blob_compression_applied = FLB_FALSE;
903
0
    int prefer_blob_compression = FLB_FALSE;
904
0
    size_t b_sent;
905
0
    void *payload_buf;
906
0
    size_t payload_size;
907
0
    struct flb_http_client *c;
908
0
    struct flb_connection *u_conn;
909
910
0
    flb_plg_debug(ctx->ins, "generated blob uri ::: %s", uri);
911
912
    /* Get upstream connection */
913
0
    u_conn = flb_upstream_conn_get(ctx->u);
914
0
    if (!u_conn) {
915
0
        flb_plg_error(ctx->ins,
916
0
                      "cannot create TCP upstream connection");
917
0
        return FLB_RETRY;
918
0
    }
919
920
0
    payload_buf = data;
921
0
    payload_size = bytes;
922
923
    /* Determine compression algorithm */
924
0
    azb_select_compression_strategy(ctx, &compression_algorithm,
925
0
                                    &prefer_blob_compression);
926
927
    /* Handle compression requests */
928
0
    if (compression_algorithm != FLB_COMPRESSION_ALGORITHM_NONE) {
929
0
        ret = azure_blob_compress_payload(compression_algorithm,
930
0
                                          (void *) data, bytes,
931
0
                                          &payload_buf, &payload_size);
932
0
        if (ret == 0) {
933
0
            compressed = FLB_TRUE;
934
0
            if (prefer_blob_compression == FLB_TRUE) {
935
0
                blob_compression_applied = FLB_TRUE;
936
0
            }
937
0
            else if (network_compression_algorithm != FLB_COMPRESSION_ALGORITHM_NONE &&
938
0
                     compression_algorithm == network_compression_algorithm) {
939
0
                network_compression_applied = FLB_TRUE;
940
0
            }
941
0
        }
942
0
        else {
943
0
            const char *alg_name;
944
945
0
            alg_name = azure_blob_compression_name(compression_algorithm);
946
0
            flb_plg_warn(ctx->ins,
947
0
                        "cannot %s payload, disabling compression",
948
0
                        alg_name);
949
0
            payload_buf = data;
950
0
            payload_size = bytes;
951
0
            compression_algorithm = FLB_COMPRESSION_ALGORITHM_NONE;
952
0
        }
953
0
    }
954
955
    /* set http header flags */
956
    /* Blob compression takes precedence over network compression since we
957
     * cannot advertise both a compressed blob content type and a transport
958
     * content-encoding at the same time. */
959
0
    if (blob_compression_applied == FLB_TRUE) {
960
0
        content_encoding = AZURE_BLOB_CE_NONE;
961
962
0
        if (compression_algorithm == FLB_COMPRESSION_ALGORITHM_ZSTD) {
963
0
            content_type = AZURE_BLOB_CT_ZSTD;
964
0
        }
965
0
        else {
966
0
            content_type = AZURE_BLOB_CT_GZIP;
967
0
        }
968
0
    }
969
0
    else if (network_compression_applied == FLB_TRUE) {
970
0
        if (network_compression_algorithm == FLB_COMPRESSION_ALGORITHM_GZIP) {
971
0
            content_encoding = AZURE_BLOB_CE_GZIP;
972
0
        }
973
0
        else if (network_compression_algorithm == FLB_COMPRESSION_ALGORITHM_ZSTD) {
974
0
            content_encoding = AZURE_BLOB_CE_ZSTD;
975
0
        }
976
0
        content_type = AZURE_BLOB_CT_JSON;
977
0
    }
978
979
    /* Create HTTP client context */
980
0
    c = flb_http_client(u_conn, FLB_HTTP_PUT,
981
0
                        uri,
982
0
                        payload_buf, payload_size, NULL, 0, NULL, 0);
983
0
    if (!c) {
984
0
        flb_plg_error(ctx->ins, "cannot create HTTP client context");
985
0
        if (compressed == FLB_TRUE) {
986
0
            flb_free(payload_buf);
987
0
        }
988
0
        flb_upstream_conn_release(u_conn);
989
0
        return FLB_RETRY;
990
0
    }
991
992
    /* Prepare headers and authentication */
993
0
    azb_http_client_setup(ctx, c, (ssize_t) payload_size, FLB_FALSE,
994
0
                          content_type, content_encoding);
995
996
    /* Send HTTP request */
997
0
    ret = flb_http_do(c, &b_sent);
998
999
    /* Release compressed buffer */
1000
0
    if (compressed == FLB_TRUE) {
1001
0
        flb_free(payload_buf);
1002
0
    }
1003
1004
    /* Validate HTTP status */
1005
0
    if (ret == -1) {
1006
0
        flb_plg_error(ctx->ins, "error sending append_blob for %s", ref_name);
1007
0
        flb_http_client_destroy(c);
1008
0
        flb_upstream_conn_release(u_conn);
1009
0
        return FLB_RETRY;
1010
0
    }
1011
1012
0
    if (c->resp.status == 201) {
1013
0
        flb_plg_info(ctx->ins, "content uploaded successfully: %s", ref_name);
1014
0
        flb_http_client_destroy(c);
1015
0
        flb_upstream_conn_release(u_conn);
1016
0
        return FLB_OK;
1017
0
    }
1018
0
    else if (c->resp.status == 404) {
1019
        /* delete "&sig=..." in the c->uri for security */
1020
0
        char *p = strstr(c->uri, "&sig=");
1021
0
        if (p) {
1022
0
            *p = '\0';
1023
0
        }
1024
1025
0
        flb_plg_info(ctx->ins, "blob not found: %s", c->uri);
1026
0
        flb_http_client_destroy(c);
1027
0
        flb_upstream_conn_release(u_conn);
1028
0
        return CREATE_BLOB;
1029
0
    }
1030
0
    else if (c->resp.payload_size > 0) {
1031
0
        flb_plg_error(ctx->ins, "http_status=%i cannot append content to blob\n%s",
1032
0
                      c->resp.status, c->resp.payload);
1033
0
        if (strstr(c->resp.payload, "must be 0 for Create Append")) {
1034
0
            flb_http_client_destroy(c);
1035
0
            flb_upstream_conn_release(u_conn);
1036
0
            return CREATE_BLOB;
1037
0
        }
1038
0
    }
1039
0
    else {
1040
0
        flb_plg_error(ctx->ins, "cannot upload %s content to blob (http_status=%i)",
1041
0
                      ref_name, c->resp.status);
1042
0
    }
1043
0
    flb_http_client_destroy(c);
1044
0
    flb_upstream_conn_release(u_conn);
1045
1046
0
    return FLB_RETRY;
1047
0
}
1048
1049
static int send_blob(struct flb_config *config,
1050
                     struct flb_input_instance *i_ins,
1051
                     struct flb_azure_blob *ctx,
1052
                     int event_type,
1053
                     int blob_type, char *name, uint64_t part_id,
1054
                     char *tag, int tag_len, const char *resolved_path_prefix,
1055
                     void *data, size_t bytes)
1056
0
{
1057
0
    int ret;
1058
0
    uint64_t ms = 0;
1059
0
    flb_sds_t uri = NULL;
1060
0
    flb_sds_t block_id = NULL;
1061
0
    flb_sds_t ref_name = NULL;
1062
0
    flb_sds_t tmp_path_prefix = NULL;
1063
0
    const char *path_prefix = resolved_path_prefix;
1064
0
    char *generated_random_string = NULL;
1065
0
    struct flb_time now;
1066
1067
0
    flb_time_get(&now);
1068
1069
0
    if (!path_prefix) {
1070
0
        if (azb_resolve_path(ctx, tag, tag_len, &now, &tmp_path_prefix) != 0) {
1071
0
            if (tmp_path_prefix) {
1072
0
                flb_sds_destroy(tmp_path_prefix);
1073
0
            }
1074
0
            return FLB_RETRY;
1075
0
        }
1076
0
        path_prefix = tmp_path_prefix;
1077
0
    }
1078
1079
0
    ref_name = flb_sds_create_size(256);
1080
0
    if (!ref_name) {
1081
0
        if (tmp_path_prefix) {
1082
0
            flb_sds_destroy(tmp_path_prefix);
1083
0
        }
1084
0
        return FLB_RETRY;
1085
0
    }
1086
1087
0
    if (blob_type == AZURE_BLOB_APPENDBLOB) {
1088
0
        uri = azb_append_blob_uri(ctx, path_prefix, tag);
1089
0
    }
1090
0
    else if (blob_type == AZURE_BLOB_BLOCKBLOB) {
1091
0
        generated_random_string = flb_malloc(ctx->blob_uri_length + 1);
1092
0
        if (!generated_random_string) {
1093
0
            flb_errno();
1094
0
            flb_plg_error(ctx->ins, "cannot allocate memory for random string");
1095
0
            flb_sds_destroy(ref_name);
1096
0
            if (tmp_path_prefix) {
1097
0
                flb_sds_destroy(tmp_path_prefix);
1098
0
            }
1099
0
            return FLB_RETRY;
1100
0
        }
1101
0
        generate_random_string_blob(generated_random_string, ctx->blob_uri_length); /* Generate the random string */
1102
0
        if (event_type == FLB_EVENT_TYPE_LOGS) {
1103
0
            block_id = azb_block_blob_id_logs(&ms);
1104
0
            if (!block_id) {
1105
0
                flb_plg_error(ctx->ins, "could not generate block id");
1106
0
                flb_free(generated_random_string);
1107
0
                generated_random_string = NULL;
1108
0
                flb_sds_destroy(ref_name);
1109
0
                if (tmp_path_prefix) {
1110
0
                    flb_sds_destroy(tmp_path_prefix);
1111
0
                }
1112
0
                return FLB_RETRY;
1113
0
            }
1114
0
            uri = azb_block_blob_uri(ctx, path_prefix, tag,
1115
0
                                     block_id, ms, generated_random_string);
1116
0
            ref_name = flb_sds_printf(&ref_name, "file=%s.%" PRIu64, name, ms);
1117
0
        }
1118
0
        else if (event_type == FLB_EVENT_TYPE_BLOBS) {
1119
0
            block_id = azb_block_blob_id_blob(ctx, name, part_id);
1120
0
            if (!block_id) {
1121
0
                flb_plg_error(ctx->ins, "could not generate block id");
1122
0
                flb_free(generated_random_string);
1123
0
                generated_random_string = NULL;
1124
0
                flb_sds_destroy(ref_name);
1125
0
                if (tmp_path_prefix) {
1126
0
                    flb_sds_destroy(tmp_path_prefix);
1127
0
                }
1128
0
                return FLB_RETRY;
1129
0
            }
1130
0
            uri = azb_block_blob_uri(ctx, path_prefix, name,
1131
0
                                     block_id, 0, generated_random_string);
1132
0
            ref_name = flb_sds_printf(&ref_name, "file=%s:%" PRIu64, name, part_id);
1133
0
        }
1134
0
    }
1135
0
    else {
1136
0
        flb_plg_error(ctx->ins, "unsupported blob type %d", blob_type);
1137
0
        flb_sds_destroy(ref_name);
1138
0
        if (tmp_path_prefix) {
1139
0
            flb_sds_destroy(tmp_path_prefix);
1140
0
        }
1141
0
        return FLB_RETRY;
1142
0
    }
1143
1144
0
    if (!uri) {
1145
0
        flb_free(generated_random_string);
1146
0
        if (block_id != NULL) {
1147
0
            flb_free(block_id);
1148
0
        }
1149
0
        flb_sds_destroy(ref_name);
1150
0
        if (tmp_path_prefix) {
1151
0
            flb_sds_destroy(tmp_path_prefix);
1152
0
        }
1153
0
        return FLB_RETRY;
1154
0
    }
1155
1156
0
    ret = http_send_blob(config, ctx, ref_name, uri, block_id, event_type, data, bytes);
1157
0
    flb_plg_debug(ctx->ins, "http_send_blob()=%i", ret);
1158
1159
0
    if (ret == FLB_OK) {
1160
        /* For Logs type, we need to commit the block right away */
1161
0
        if (blob_type == AZURE_BLOB_BLOCKBLOB &&
1162
0
            event_type == FLB_EVENT_TYPE_LOGS) {
1163
0
            ret = azb_block_blob_commit_block(ctx, path_prefix, block_id,
1164
0
                                              tag, ms, generated_random_string);
1165
0
        }
1166
0
    }
1167
0
    else if (ret == CREATE_BLOB) {
1168
0
        ret = create_blob(ctx, path_prefix, name);
1169
0
        if (ret == FLB_OK) {
1170
0
            ret = http_send_blob(config, ctx, ref_name, uri, block_id, event_type, data, bytes);
1171
0
            if (ret == FLB_OK &&
1172
0
                blob_type == AZURE_BLOB_BLOCKBLOB &&
1173
0
                event_type == FLB_EVENT_TYPE_LOGS) {
1174
0
                ret = azb_block_blob_commit_block(ctx, path_prefix, block_id,
1175
0
                                                  tag, ms, generated_random_string);
1176
0
            }
1177
0
        }
1178
0
    }
1179
0
    flb_sds_destroy(ref_name);
1180
1181
0
    flb_sds_destroy(uri);
1182
0
    if (generated_random_string) {
1183
0
        flb_free(generated_random_string);
1184
0
    }
1185
1186
0
    if (block_id != NULL) {
1187
0
        flb_free(block_id);
1188
0
    }
1189
1190
0
    if (tmp_path_prefix) {
1191
0
        flb_sds_destroy(tmp_path_prefix);
1192
0
    }
1193
1194
0
    return ret;
1195
0
}
1196
1197
static int create_container(struct flb_azure_blob *ctx, char *name)
1198
0
{
1199
0
    int ret;
1200
0
    size_t b_sent;
1201
0
    flb_sds_t uri;
1202
0
    struct flb_http_client *c;
1203
0
    struct flb_connection *u_conn;
1204
1205
    /* Get upstream connection */
1206
0
    u_conn = flb_upstream_conn_get(ctx->u);
1207
0
    if (!u_conn) {
1208
0
        flb_plg_error(ctx->ins,
1209
0
                      "cannot create upstream connection for container creation");
1210
0
        return FLB_FALSE;
1211
0
    }
1212
1213
    /* URI */
1214
0
    uri = azb_uri_ensure_or_create_container(ctx);
1215
0
    if (!uri) {
1216
0
        flb_upstream_conn_release(u_conn);
1217
0
        return FLB_FALSE;
1218
0
    }
1219
1220
    /* Create HTTP client context */
1221
0
    c = flb_http_client(u_conn, FLB_HTTP_PUT,
1222
0
                        uri,
1223
0
                        NULL, 0, NULL, 0, NULL, 0);
1224
0
    if (!c) {
1225
0
        flb_plg_error(ctx->ins, "cannot create HTTP client context");
1226
0
        flb_upstream_conn_release(u_conn);
1227
0
        return FLB_FALSE;
1228
0
    }
1229
1230
    /* Prepare headers and authentication */
1231
0
    azb_http_client_setup(ctx, c, -1, FLB_FALSE,
1232
0
                          AZURE_BLOB_CT_NONE, AZURE_BLOB_CE_NONE);
1233
1234
    /* Send HTTP request */
1235
0
    ret = flb_http_do(c, &b_sent);
1236
1237
    /* Release URI */
1238
0
    flb_sds_destroy(uri);
1239
1240
    /* Validate http response */
1241
0
    if (ret == -1) {
1242
0
        flb_plg_error(ctx->ins, "error requesting container creation");
1243
0
        flb_http_client_destroy(c);
1244
0
        flb_upstream_conn_release(u_conn);
1245
0
        return FLB_FALSE;
1246
0
    }
1247
1248
0
    if (c->resp.status == 201) {
1249
0
        flb_plg_info(ctx->ins, "container '%s' created sucessfully", name);
1250
0
    }
1251
0
    else {
1252
0
        if (c->resp.payload_size > 0) {
1253
0
            flb_plg_error(ctx->ins, "cannot create container '%s'\n%s",
1254
0
                          name, c->resp.payload);
1255
0
        }
1256
0
        else {
1257
0
            flb_plg_error(ctx->ins, "cannot create container '%s'\n%s",
1258
0
                          name, c->resp.payload);
1259
0
        }
1260
0
        flb_http_client_destroy(c);
1261
0
        flb_upstream_conn_release(u_conn);
1262
0
        return FLB_FALSE;
1263
0
    }
1264
1265
0
    flb_http_client_destroy(c);
1266
0
    flb_upstream_conn_release(u_conn);
1267
0
    return FLB_TRUE;
1268
0
}
1269
1270
/*
1271
 * Check that the container exists, if it doesn't and the configuration property
1272
 * auto_create_container is enabled, it will send a request to create it. If it
1273
 * could not be created, it returns FLB_FALSE.
1274
 * If auto_create_container is disabled, it will return FLB_TRUE assuming the container
1275
 * already exists.
1276
 */
1277
static int ensure_container(struct flb_azure_blob *ctx)
1278
0
{
1279
0
    int ret;
1280
0
    int status;
1281
0
    size_t b_sent;
1282
0
    flb_sds_t uri;
1283
0
    struct flb_http_client *c;
1284
0
    struct flb_connection *u_conn;
1285
1286
0
    if (!ctx->auto_create_container) {
1287
0
        flb_plg_info(ctx->ins, "auto_create_container is disabled, assuming container '%s' already exists",
1288
0
                     ctx->container_name);
1289
0
        return FLB_TRUE;
1290
0
    }
1291
1292
0
    uri = azb_uri_ensure_or_create_container(ctx);
1293
0
    if (!uri) {
1294
0
        flb_plg_error(ctx->ins, "cannot create container URI");
1295
0
        return FLB_FALSE;
1296
0
    }
1297
1298
    /* Get upstream connection */
1299
0
    u_conn = flb_upstream_conn_get(ctx->u);
1300
0
    if (!u_conn) {
1301
0
        flb_plg_error(ctx->ins,
1302
0
                      "cannot create upstream connection for container check");
1303
0
        flb_sds_destroy(uri);
1304
0
        return FLB_FALSE;
1305
0
    }
1306
1307
    /* Create HTTP client context */
1308
0
    c = flb_http_client(u_conn, FLB_HTTP_GET,
1309
0
                        uri,
1310
0
                        NULL, 0, NULL, 0, NULL, 0);
1311
0
    if (!c) {
1312
0
        flb_plg_error(ctx->ins, "cannot create HTTP client context");
1313
0
        flb_upstream_conn_release(u_conn);
1314
0
        return FLB_FALSE;
1315
0
    }
1316
0
    flb_http_strip_port_from_host(c);
1317
1318
    /* Prepare headers and authentication */
1319
0
    azb_http_client_setup(ctx, c, -1, FLB_FALSE,
1320
0
                          AZURE_BLOB_CT_NONE, AZURE_BLOB_CE_NONE);
1321
1322
    /* Send HTTP request */
1323
0
    ret = flb_http_do(c, &b_sent);
1324
0
    flb_sds_destroy(uri);
1325
1326
0
    if (ret == -1) {
1327
0
        flb_plg_error(ctx->ins, "error requesting container properties");
1328
0
        flb_upstream_conn_release(u_conn);
1329
0
        return FLB_FALSE;
1330
0
    }
1331
1332
0
    status = c->resp.status;
1333
0
    flb_http_client_destroy(c);
1334
1335
    /* Release connection */
1336
0
    flb_upstream_conn_release(u_conn);
1337
1338
    /* Request was successful, validate HTTP status code */
1339
0
    if (status == 404) {
1340
        /* The container was not found, try to create it */
1341
0
        flb_plg_info(ctx->ins, "container '%s' not found, trying to create it",
1342
0
                     ctx->container_name);
1343
0
        ret = create_container(ctx, ctx->container_name);
1344
0
        return ret;
1345
0
    }
1346
0
    else if (status == 200) {
1347
0
        flb_plg_info(ctx->ins, "container '%s' already exists", ctx->container_name);
1348
0
        return FLB_TRUE;
1349
0
    }
1350
0
    else if (status == 403) {
1351
0
        flb_plg_error(ctx->ins, "failed getting container '%s', access denied",
1352
0
                      ctx->container_name);
1353
0
        return FLB_FALSE;
1354
0
    }
1355
    
1356
0
    flb_plg_error(ctx->ins, "get container request failed, status=%i",
1357
0
                  status);
1358
1359
0
    return FLB_FALSE;
1360
0
}
1361
1362
/*
1363
 * Create the output context, buffered store, and mutex shared by workers.
1364
 */
1365
static int cb_azure_blob_init(struct flb_output_instance *ins,
1366
                              struct flb_config *config, void *data)
1367
0
{
1368
0
    struct flb_azure_blob *ctx = NULL;
1369
0
    (void) ins;
1370
0
    (void) config;
1371
0
    (void) data;
1372
1373
0
    FLB_TLS_INIT(worker_info);
1374
1375
0
    ctx = flb_azure_blob_conf_create(ins, config);
1376
0
    if (!ctx) {
1377
0
        return -1;
1378
0
    }
1379
1380
0
    if (ctx->buffering_enabled == FLB_TRUE) {
1381
0
        ctx->ins = ins;
1382
0
        ctx->retry_time = 0;
1383
1384
        /* Initialize local storage */
1385
0
        int ret = azure_blob_store_init(ctx);
1386
0
        if (ret == -1) {
1387
0
            flb_plg_error(ctx->ins, "Failed to initialize kusto storage: %s",
1388
0
                          ctx->store_dir);
1389
0
            return -1;
1390
0
        }
1391
1392
        /* validate 'total_file_size' */
1393
0
        if (ctx->file_size <= 0) {
1394
0
            flb_plg_error(ctx->ins, "Failed to parse upload_file_size");
1395
0
            return -1;
1396
0
        }
1397
0
        if (ctx->file_size < 1000000) {
1398
0
            flb_plg_error(ctx->ins, "upload_file_size must be at least 1MB");
1399
0
            return -1;
1400
0
        }
1401
0
        if (ctx->file_size > MAX_FILE_SIZE) {
1402
0
            flb_plg_error(ctx->ins, "Max total_file_size must be lower than %ld bytes", MAX_FILE_SIZE);
1403
0
            return -1;
1404
0
        }
1405
0
        ctx->has_old_buffers = azure_blob_store_has_data(ctx);
1406
0
        ctx->timer_created = FLB_FALSE;
1407
0
        ctx->timer_ms = (int) (ctx->upload_timeout / 6) * 1000;
1408
0
        flb_plg_info(ctx->ins, "Using upload size %lu bytes", ctx->file_size);
1409
0
    }
1410
1411
0
    if (pthread_mutex_init(&ctx->buffer_mutex, NULL) != 0) {
1412
0
        azure_blob_store_exit(ctx);
1413
0
        flb_azure_blob_conf_destroy(ctx);
1414
0
        return -1;
1415
0
    }
1416
1417
0
    flb_output_set_context(ins, ctx);
1418
1419
0
    flb_output_set_http_debug_callbacks(ins);
1420
0
    return 0;
1421
0
}
1422
1423
static int blob_chunk_register_parts(struct flb_azure_blob *ctx, uint64_t file_id, size_t total_size)
1424
0
{
1425
0
    int ret;
1426
0
    int64_t parts = 0;
1427
0
    int64_t id;
1428
0
    size_t offset_start = 0;
1429
0
    size_t offset_end = 0;
1430
1431
    /* generate file parts */
1432
0
    while (offset_start < total_size) {
1433
0
        offset_end = offset_start + ctx->part_size;
1434
1435
        /* do not exceed maximum size */
1436
0
        if (offset_end > total_size) {
1437
0
            offset_end = total_size;
1438
0
        }
1439
1440
        /* insert part */
1441
0
        ret = azb_db_file_part_insert(ctx, file_id, parts, offset_start, offset_end, &id);
1442
0
        if (ret == -1) {
1443
0
            flb_plg_error(ctx->ins, "cannot insert blob file part into database");
1444
0
            return -1;
1445
0
        }
1446
0
        offset_start = offset_end;
1447
0
        parts++;
1448
0
    }
1449
1450
0
    return parts;
1451
0
}
1452
1453
static int process_blob_chunk(struct flb_azure_blob *ctx, struct flb_event_chunk *event_chunk)
1454
0
{
1455
0
    int64_t ret;
1456
0
    int64_t file_id;
1457
0
    cfl_sds_t file_path = NULL;
1458
0
    cfl_sds_t source = NULL;
1459
0
    size_t file_size;
1460
0
    msgpack_object map;
1461
1462
0
    struct flb_log_event_decoder log_decoder;
1463
0
    struct flb_log_event         log_event;
1464
1465
0
    if (ctx->db == NULL) {
1466
0
        flb_plg_error(ctx->ins, "Cannot process blob because this operation requires a database.");
1467
1468
0
        return -1;
1469
0
    }
1470
1471
0
    ret = flb_log_event_decoder_init(&log_decoder,
1472
0
                                    (char *) event_chunk->data,
1473
0
                                     event_chunk->size);
1474
1475
0
    if (ret != FLB_EVENT_DECODER_SUCCESS) {
1476
0
        flb_plg_error(ctx->ins,
1477
0
                    "Log event decoder initialization error : %i", (int) ret);
1478
0
        return -1;
1479
1480
0
    }
1481
1482
0
    while (flb_log_event_decoder_next(&log_decoder, &log_event) == FLB_EVENT_DECODER_SUCCESS) {
1483
0
        map = *log_event.body;
1484
0
        ret = flb_input_blob_file_get_info(map, &source, &file_path, &file_size);
1485
0
        if (ret == -1) {
1486
0
            flb_plg_error(ctx->ins, "cannot get file info from blob record, skipping");
1487
0
            continue;
1488
0
        }
1489
1490
0
        flb_sds_t path_prefix = NULL;
1491
1492
0
        int tag_len = 0;
1493
1494
0
        if (event_chunk->tag) {
1495
0
            tag_len = flb_sds_len(event_chunk->tag);
1496
0
        }
1497
1498
0
        ret = azb_resolve_path(ctx,
1499
0
                               event_chunk->tag,
1500
0
                               tag_len,
1501
0
                               &log_event.timestamp,
1502
0
                               &path_prefix);
1503
0
        if (ret != 0) {
1504
0
            flb_plg_error(ctx->ins, "cannot resolve blob path prefix, skipping file %s",
1505
0
                          file_path);
1506
0
            if (path_prefix) {
1507
0
                flb_sds_destroy(path_prefix);
1508
0
            }
1509
0
            cfl_sds_destroy(file_path);
1510
0
            cfl_sds_destroy(source);
1511
0
            continue;
1512
0
        }
1513
1514
0
        ret = azb_db_file_insert(ctx, source, ctx->real_endpoint, file_path,
1515
0
                                 path_prefix, file_size);
1516
1517
0
        if (ret == -1) {
1518
0
            flb_plg_error(ctx->ins, "cannot insert blob file into database: %s (size=%lu)",
1519
0
                          file_path, file_size);
1520
0
            cfl_sds_destroy(file_path);
1521
0
            cfl_sds_destroy(source);
1522
0
            if (path_prefix) {
1523
0
                flb_sds_destroy(path_prefix);
1524
0
            }
1525
0
            continue;
1526
0
        }
1527
0
        if (path_prefix) {
1528
0
            flb_sds_destroy(path_prefix);
1529
0
        }
1530
0
        cfl_sds_destroy(file_path);
1531
0
        cfl_sds_destroy(source);
1532
1533
        /* generate the parts by using the newest id created (ret) */
1534
0
        file_id = ret;
1535
0
        ret = blob_chunk_register_parts(ctx, file_id, file_size);
1536
0
        if (ret == -1) {
1537
0
            flb_plg_error(ctx->ins, "cannot register blob file '%s 'parts into database",
1538
0
                            file_path);
1539
0
            return -1;
1540
0
        }
1541
1542
0
        flb_plg_debug(ctx->ins, "blob file '%s' (id=%zu) registered with %zu parts",
1543
0
                      file_path, file_id, ret);
1544
0
    }
1545
1546
0
    flb_log_event_decoder_destroy(&log_decoder);
1547
0
    return 0;
1548
0
}
1549
1550
static void cb_azb_blob_file_upload(struct flb_config *config, void *out_context)
1551
0
{
1552
0
    int ret;
1553
0
    char *out_buf = NULL;
1554
0
    size_t out_size;
1555
0
    uint64_t id;
1556
0
    uint64_t file_id;
1557
0
    uint64_t part_id;
1558
0
    uint64_t part_delivery_attempts;
1559
0
    uint64_t file_delivery_attempts;
1560
0
    off_t offset_start;
1561
0
    off_t offset_end;
1562
0
    cfl_sds_t file_destination = NULL;
1563
0
    cfl_sds_t file_path = NULL;
1564
0
    cfl_sds_t part_ids = NULL;
1565
0
    cfl_sds_t source = NULL;
1566
0
    cfl_sds_t path_prefix = NULL;
1567
0
    cfl_sds_t part_path_prefix = NULL;
1568
0
    cfl_sds_t stale_path_prefix = NULL;
1569
0
    cfl_sds_t aborted_path_prefix = NULL;
1570
0
    struct flb_azure_blob *ctx = out_context;
1571
0
    struct worker_info *info;
1572
0
    struct flb_blob_delivery_notification *notification;
1573
1574
0
    info = FLB_TLS_GET(worker_info);
1575
1576
0
    if (info->active_upload) {
1577
0
        flb_plg_trace(ctx->ins, "[worker: file upload] upload already in progress...");
1578
0
        flb_sched_timer_cb_coro_return();
1579
0
    }
1580
1581
0
    if (ctx->db == NULL) {
1582
0
        flb_sched_timer_cb_coro_return();
1583
0
    }
1584
1585
0
    info->active_upload = FLB_TRUE;
1586
1587
    /*
1588
     * Check if is there any file which has been fully uploaded and we need to commit it with
1589
     * the Put Block List operation
1590
     */
1591
1592
0
    pthread_mutex_lock(&ctx->file_upload_commit_file_parts);
1593
1594
0
    while (1) {
1595
0
        ret = azb_db_file_get_next_stale(ctx,
1596
0
                                         &file_id,
1597
0
                                         &file_path,
1598
0
                                         &stale_path_prefix);
1599
1600
0
        if (ret == 1) {
1601
0
            delete_blob(ctx, stale_path_prefix, file_path);
1602
1603
0
            azb_db_file_reset_upload_states(ctx, file_id, file_path);
1604
0
            azb_db_file_set_aborted_state(ctx, file_id, file_path, 0);
1605
1606
0
            cfl_sds_destroy(file_path);
1607
0
            if (stale_path_prefix) {
1608
0
                cfl_sds_destroy(stale_path_prefix);
1609
0
                stale_path_prefix = NULL;
1610
0
            }
1611
1612
0
            file_path = NULL;
1613
0
        }
1614
0
        else {
1615
0
            break;
1616
0
        }
1617
0
    }
1618
1619
0
    while (1) {
1620
0
        ret = azb_db_file_get_next_aborted(ctx,
1621
0
                                           &file_id,
1622
0
                                           &file_delivery_attempts,
1623
0
                                           &file_path,
1624
0
                                           &source,
1625
0
                                           &aborted_path_prefix);
1626
1627
0
        if (ret == 1) {
1628
0
            ret = delete_blob(ctx, aborted_path_prefix, file_path);
1629
1630
0
            if (ctx->file_delivery_attempt_limit != FLB_OUT_RETRY_UNLIMITED &&
1631
0
                file_delivery_attempts < ctx->file_delivery_attempt_limit) {
1632
0
                azb_db_file_reset_upload_states(ctx, file_id, file_path);
1633
0
                azb_db_file_set_aborted_state(ctx, file_id, file_path, 0);
1634
0
            }
1635
0
            else {
1636
0
                ret = azb_db_file_delete(ctx, file_id, file_path);
1637
1638
0
                notification = flb_calloc(1,
1639
0
                                        sizeof(
1640
0
                                            struct flb_blob_delivery_notification));
1641
1642
0
                if (notification != NULL) {
1643
0
                    notification->base.dynamically_allocated = FLB_TRUE;
1644
0
                    notification->base.notification_type = FLB_NOTIFICATION_TYPE_BLOB_DELIVERY;
1645
0
                    notification->base.destructor = flb_input_blob_delivery_notification_destroy;
1646
0
                    notification->success = FLB_FALSE;
1647
0
                    notification->path = cfl_sds_create(file_path);
1648
1649
0
                    ret = flb_notification_enqueue(FLB_PLUGIN_INPUT,
1650
0
                                                source,
1651
0
                                                &notification->base,
1652
0
                                                config);
1653
1654
0
                    if (ret != 0) {
1655
0
                        flb_plg_error(ctx->ins,
1656
0
                                    "blob file '%s' (id=%" PRIu64 ") notification " \
1657
0
                                    "delivery error %d", file_path, file_id, ret);
1658
1659
0
                        flb_notification_cleanup(&notification->base);
1660
0
                    }
1661
0
                }
1662
0
            }
1663
1664
0
            cfl_sds_destroy(file_path);
1665
0
            cfl_sds_destroy(source);
1666
0
            if (aborted_path_prefix) {
1667
0
                cfl_sds_destroy(aborted_path_prefix);
1668
0
                aborted_path_prefix = NULL;
1669
0
            }
1670
1671
0
            file_path = NULL;
1672
0
            source = NULL;
1673
0
        }
1674
0
        else {
1675
0
            break;
1676
0
        }
1677
0
    }
1678
1679
0
    ret = azb_db_file_oldest_ready(ctx, &file_id, &file_path, &part_ids, &source,
1680
0
                                   &path_prefix);
1681
0
    if (ret == 0) {
1682
0
        flb_plg_trace(ctx->ins, "no blob files ready to commit");
1683
0
    }
1684
0
    else if (ret == -1) {
1685
0
        flb_plg_error(ctx->ins, "cannot get oldest blob file ready to upload");
1686
0
    }
1687
0
    else if (ret == 1) {
1688
        /* one file is ready to be committed */
1689
0
        flb_plg_debug(ctx->ins, "blob file '%s' (id=%" PRIu64 ") ready to upload", file_path, file_id);
1690
0
        const char *commit_prefix = azb_commit_prefix_with_fallback(ctx, path_prefix);
1691
1692
0
        ret = azb_block_blob_commit_file_parts(ctx, file_id, file_path, part_ids, commit_prefix);
1693
0
        if (ret == -1) {
1694
0
            flb_plg_error(ctx->ins, "cannot commit blob file parts for file id=%" PRIu64 " path=%s",
1695
0
                          file_id, file_path);
1696
0
        }
1697
0
        else {
1698
0
            flb_plg_info(ctx->ins, "blob file '%s' (id=%" PRIu64 ") committed successfully", file_path, file_id);
1699
            /* notify the engine the blob file has been processed */
1700
            /* FIXME! */
1701
1702
0
            notification = flb_calloc(1,
1703
0
                                    sizeof(
1704
0
                                        struct flb_blob_delivery_notification));
1705
1706
0
            if (notification != NULL) {
1707
0
                notification->base.dynamically_allocated = FLB_TRUE;
1708
0
                notification->base.notification_type = FLB_NOTIFICATION_TYPE_BLOB_DELIVERY;
1709
0
                notification->base.destructor = flb_input_blob_delivery_notification_destroy;
1710
0
                notification->success = FLB_TRUE;
1711
0
                notification->path = cfl_sds_create(file_path);
1712
1713
0
                ret = flb_notification_enqueue(FLB_PLUGIN_INPUT,
1714
0
                                               source,
1715
0
                                               &notification->base,
1716
0
                                               config);
1717
1718
0
                if (ret != 0) {
1719
0
                    flb_plg_error(ctx->ins,
1720
0
                                "blob file '%s' (id=%" PRIu64 ") notification " \
1721
0
                                "delivery error %d", file_path, file_id, ret);
1722
1723
0
                    flb_notification_cleanup(&notification->base);
1724
0
                }
1725
0
            }
1726
1727
            /* remove the file entry from the database */
1728
0
            ret = azb_db_file_delete(ctx, file_id, file_path);
1729
0
            if (ret == -1) {
1730
0
                flb_plg_error(ctx->ins, "cannot delete blob file '%s' (id=%" PRIu64 ") from the database",
1731
0
                              file_path, file_id);
1732
0
            }
1733
0
        }
1734
0
    }
1735
0
    pthread_mutex_unlock(&ctx->file_upload_commit_file_parts);
1736
1737
0
    if (file_path) {
1738
0
        cfl_sds_destroy(file_path);
1739
0
    }
1740
0
    if (part_ids) {
1741
0
        cfl_sds_destroy(part_ids);
1742
0
    }
1743
0
    if (source) {
1744
0
        cfl_sds_destroy(source);
1745
0
    }
1746
0
    if (path_prefix) {
1747
0
        cfl_sds_destroy(path_prefix);
1748
0
        path_prefix = NULL;
1749
0
    }
1750
1751
    /* check for a next part file and lock it */
1752
0
    ret = azb_db_file_part_get_next(ctx, &id, &file_id, &part_id,
1753
0
                                    &offset_start, &offset_end,
1754
0
                                    &part_delivery_attempts,
1755
0
                                    &file_delivery_attempts,
1756
0
                                    &file_path,
1757
0
                                    &file_destination,
1758
0
                                    &part_path_prefix);
1759
0
    if (ret == -1) {
1760
0
        flb_plg_error(ctx->ins, "cannot get next blob file part");
1761
0
        info->active_upload = FLB_FALSE;
1762
0
        flb_sched_timer_cb_coro_return();
1763
0
    }
1764
0
    else if (ret == 0) {
1765
0
        flb_plg_trace(ctx->ins, "no more blob file parts to process");
1766
0
        info->active_upload = FLB_FALSE;
1767
0
        flb_sched_timer_cb_coro_return();
1768
0
    }
1769
0
    else if (ret == 1) {
1770
        /* just continue, the row info was retrieved */
1771
0
    }
1772
1773
1774
0
    if (strcmp(file_destination, ctx->real_endpoint) != 0) {
1775
0
        flb_plg_info(ctx->ins,
1776
0
                     "endpoint change detected, restarting file : %s\n%s\n%s",
1777
0
                     file_path,
1778
0
                     file_destination,
1779
0
                     ctx->real_endpoint);
1780
1781
0
        info->active_upload = FLB_FALSE;
1782
1783
        /* we need to set the aborted state flag to wait for existing uploads
1784
         * to finish and then wipe the slate and start again but we don't want
1785
         * to increment the failure count in this case.
1786
         */
1787
0
        azb_db_file_set_aborted_state(ctx, file_id, file_path, 1);
1788
1789
0
        cfl_sds_destroy(file_path);
1790
0
        cfl_sds_destroy(file_destination);
1791
0
        if (part_path_prefix) {
1792
0
            cfl_sds_destroy(part_path_prefix);
1793
0
            part_path_prefix = NULL;
1794
0
        }
1795
1796
0
        flb_sched_timer_cb_coro_return();
1797
0
    }
1798
1799
    /* since this is the first part we want to increment the files
1800
     * delivery attempt counter.
1801
     */
1802
0
    if (part_id == 0) {
1803
0
        ret = azb_db_file_delivery_attempts(ctx, file_id, ++file_delivery_attempts);
1804
0
    }
1805
1806
    /* read the file content */
1807
0
    ret = flb_utils_read_file_offset(file_path, offset_start, offset_end, &out_buf, &out_size);
1808
0
    if (ret == -1) {
1809
0
        flb_plg_error(ctx->ins, "cannot read file part %s", file_path);
1810
1811
0
        info->active_upload = FLB_FALSE;
1812
1813
0
        cfl_sds_destroy(file_path);
1814
0
        cfl_sds_destroy(file_destination);
1815
0
        if (part_path_prefix) {
1816
0
            cfl_sds_destroy(part_path_prefix);
1817
0
            part_path_prefix = NULL;
1818
0
        }
1819
1820
0
        flb_sched_timer_cb_coro_return();
1821
0
    }
1822
1823
0
    azb_db_file_part_delivery_attempts(ctx, file_id, part_id, ++part_delivery_attempts);
1824
1825
0
    flb_plg_debug(ctx->ins, "sending part file %s (id=%" PRIu64 " part_id=%" PRIu64 ")", file_path, id, part_id);
1826
1827
0
    ret = send_blob(config, NULL, ctx, FLB_EVENT_TYPE_BLOBS,
1828
0
                    AZURE_BLOB_BLOCKBLOB, file_path, part_id,
1829
0
                    NULL, 0, part_path_prefix, out_buf, out_size);
1830
1831
0
    if (ret == FLB_OK) {
1832
0
        ret = azb_db_file_part_uploaded(ctx, id);
1833
1834
0
        if (ret == -1) {
1835
0
            info->active_upload = FLB_FALSE;
1836
1837
0
            cfl_sds_destroy(file_path);
1838
0
            cfl_sds_destroy(file_destination);
1839
0
            if (part_path_prefix) {
1840
0
                cfl_sds_destroy(part_path_prefix);
1841
0
                part_path_prefix = NULL;
1842
0
            }
1843
1844
0
            flb_sched_timer_cb_coro_return();
1845
0
        }
1846
0
    }
1847
0
    else if (ret == FLB_RETRY) {
1848
0
        azb_db_file_part_in_progress(ctx, 0, id);
1849
1850
0
        if (ctx->part_delivery_attempt_limit != FLB_OUT_RETRY_UNLIMITED &&
1851
0
            part_delivery_attempts >= ctx->part_delivery_attempt_limit) {
1852
0
            azb_db_file_set_aborted_state(ctx, file_id, file_path, 1);
1853
0
        }
1854
0
    }
1855
1856
0
    info->active_upload = FLB_FALSE;
1857
1858
0
    if (out_buf) {
1859
0
        flb_free(out_buf);
1860
0
    }
1861
1862
0
    cfl_sds_destroy(file_path);
1863
0
    cfl_sds_destroy(file_destination);
1864
0
    if (part_path_prefix) {
1865
0
        cfl_sds_destroy(part_path_prefix);
1866
0
        part_path_prefix = NULL;
1867
0
    }
1868
1869
0
    flb_sched_timer_cb_coro_return();
1870
0
}
1871
1872
static int azb_timer_create(struct flb_azure_blob *ctx)
1873
0
{
1874
0
    int ret;
1875
0
    int64_t ms;
1876
0
    struct flb_sched *sched;
1877
1878
0
    sched = flb_sched_ctx_get();
1879
1880
    /* convert from seconds to milliseconds (scheduler needs ms) */
1881
0
    ms = ctx->upload_parts_timeout * 1000;
1882
1883
0
    ret = flb_sched_timer_coro_cb_create(sched, FLB_SCHED_TIMER_CB_PERM, ms,
1884
0
                                         cb_azb_blob_file_upload, ctx, NULL);
1885
0
    if (ret == -1) {
1886
0
        flb_plg_error(ctx->ins, "failed to create upload timer");
1887
0
        return -1;
1888
0
    }
1889
1890
0
    return 0;
1891
0
}
1892
1893
/*
1894
 * Wait without blocking other workers from buffering. The caller holds
1895
 * buffer_mutex, and the file stays locked until this callback finishes with it.
1896
 * Return a refreshed list cursor because other files may have been removed
1897
 * while the mutex was released.
1898
 */
1899
static struct mk_list *azure_blob_retry_wait(struct flb_azure_blob *ctx,
1900
                                   struct azure_blob_file *file,
1901
                                   unsigned int delay)
1902
0
{
1903
0
    azure_blob_store_file_lock(file);
1904
0
    pthread_mutex_unlock(&ctx->buffer_mutex);
1905
0
    sleep(delay);
1906
0
    pthread_mutex_lock(&ctx->buffer_mutex);
1907
1908
0
    return file->fsf->_head.next;
1909
0
}
1910
1911
/**
1912
 * Azure Blob Storage ingestion callback function
1913
 * This function handles the upload of data chunks to Azure Blob Storage with retry mechanism
1914
 * @param config: Fluent Bit configuration
1915
 * @param data: Azure Blob context data
1916
 */
1917
0
static void cb_azure_blob_ingest(struct flb_config *config, void *data) {
1918
    /* Initialize context and file handling variables */
1919
0
    struct flb_azure_blob *ctx = data;
1920
0
    struct azure_blob_file *file = NULL;
1921
0
    struct flb_fstore_file *fsf;
1922
0
    char *buffer = NULL;
1923
0
    size_t buffer_size = 0;
1924
0
    struct mk_list *tmp;
1925
0
    struct mk_list *head;
1926
0
    int ret;
1927
0
    time_t now;
1928
0
    flb_sds_t payload;
1929
0
    flb_sds_t tag_sds;
1930
1931
    /* Retry mechanism configuration */
1932
0
    int retry_count;
1933
0
    int backoff_time;
1934
0
    const int max_backoff_time = 64;  /* Maximum backoff time in seconds */
1935
1936
0
    pthread_mutex_lock(&ctx->buffer_mutex);
1937
1938
    /* Log entry point and container information */
1939
0
    flb_plg_debug(ctx->ins, "Running upload timer callback (cb_azure_blob_ingest)..");
1940
1941
    /* Initialize jitter for retry mechanism */
1942
0
    srand(time(NULL));
1943
0
    now = time(NULL);
1944
1945
    /* Iterate through all chunks in the active stream */
1946
0
    mk_list_foreach_safe(head, tmp, &ctx->stream_active->files) {
1947
0
        fsf = mk_list_entry(head, struct flb_fstore_file, _head);
1948
0
        file = fsf->data;
1949
1950
        /* Debug logging for current file processing */
1951
0
        flb_plg_debug(ctx->ins, "Iterating files inside upload timer callback (cb_azure_blob_ingest).. %s",
1952
0
                      file->fsf->name);
1953
1954
        /* Skip if chunk hasn't timed out yet */
1955
0
        if (now < (file->create_time + ctx->upload_timeout + ctx->retry_time)) {
1956
0
            continue;
1957
0
        }
1958
1959
        /* Skip if file is already being processed */
1960
0
        flb_plg_debug(ctx->ins, "cb_azure_blob_ingest :: Before file locked check %s", file->fsf->name);
1961
0
        if (file->locked == FLB_TRUE) {
1962
0
            continue;
1963
0
        }
1964
1965
        /* Initialize retry mechanism parameters */
1966
0
        retry_count = 0;
1967
0
        backoff_time = 2;  /* Initial backoff time in seconds */
1968
1969
        /* Retry loop for upload attempts */
1970
0
        while (retry_count < ctx->scheduler_max_retries) {
1971
            /* Construct request buffer for upload */
1972
0
            flb_plg_debug(ctx->ins, "cb_azure_blob_ingest :: Before construct_request_buffer %s", file->fsf->name);
1973
0
            ret = construct_request_buffer(ctx, NULL, file, &buffer, &buffer_size);
1974
1975
            /* Handle request buffer construction failure */
1976
0
            if (ret < 0) {
1977
0
                flb_plg_error(ctx->ins, "cb_azure_blob_ingest :: Could not construct request buffer for %s",
1978
0
                              file->fsf->name);
1979
0
                retry_count++;
1980
1981
                /* Implement exponential backoff with jitter */
1982
0
                int jitter = rand() % backoff_time;
1983
0
                flb_plg_warn(ctx->ins, "cb_azure_blob_ingest :: failure in construct_request_buffer :: Retrying in %d seconds (attempt %d of %d) with jitter %d for file %s",
1984
0
                             backoff_time + jitter, retry_count, ctx->scheduler_max_retries, jitter, file->fsf->name);
1985
0
                tmp = azure_blob_retry_wait(ctx, file, backoff_time + jitter);
1986
0
                backoff_time = (backoff_time * 2 < max_backoff_time) ? backoff_time * 2 : max_backoff_time;
1987
0
                continue;
1988
0
            }
1989
1990
            /* Create payload and tags for blob upload */
1991
0
            payload = flb_sds_create_len(buffer, buffer_size);
1992
0
            tag_sds = flb_sds_create(fsf->meta_buf);
1993
0
            flb_plg_debug(ctx->ins, "cb_azure_blob_ingest ::: tag of the file %s", tag_sds);
1994
1995
            /* Keep the file owned while other workers buffer during the upload. */
1996
0
            pthread_mutex_unlock(&ctx->buffer_mutex);
1997
0
            ret = send_blob(config, NULL, ctx, FLB_EVENT_TYPE_LOGS,ctx->btype , (char *) tag_sds,0, (char *) tag_sds,
1998
0
                            flb_sds_len(tag_sds), NULL, payload, flb_sds_len(payload));
1999
0
            pthread_mutex_lock(&ctx->buffer_mutex);
2000
0
            tmp = fsf->_head.next;
2001
2002
            /* Handle blob send failure */
2003
0
            if (ret != FLB_OK) {
2004
                /* Clean up resources and update failure count */
2005
0
                flb_plg_error(ctx->ins, "cb_azure_blob_ingest :: Failed to ingest data to Azure Blob Storage (attempt %d of %d)",
2006
0
                              retry_count + 1, ctx->scheduler_max_retries);
2007
0
                flb_free(buffer);
2008
0
                flb_sds_destroy(payload);
2009
0
                flb_sds_destroy(tag_sds);
2010
2011
0
                if (file) {
2012
0
                    azure_blob_store_file_unlock(file);
2013
0
                    file->failures += 1;
2014
0
                }
2015
2016
0
                retry_count++;
2017
2018
                /* Implement exponential backoff with jitter for retry */
2019
0
                int jitter = rand() % backoff_time;
2020
0
                flb_plg_warn(ctx->ins, "cb_azure_blob_ingest :: error sending blob :: Retrying in %d seconds (attempt %d of %d) with jitter %d for file %s",
2021
0
                             backoff_time + jitter, retry_count, ctx->scheduler_max_retries, jitter, file->fsf->name);
2022
0
                tmp = azure_blob_retry_wait(ctx, file, backoff_time + jitter);
2023
0
                backoff_time = (backoff_time * 2 < max_backoff_time) ? backoff_time * 2 : max_backoff_time;
2024
0
                continue;
2025
0
            }
2026
2027
            /* Handle successful upload */
2028
0
            flb_plg_debug(ctx->ins, "cb_azure_blob_ingest :: deleting successfully ingested file %s",
2029
0
                          fsf->name);
2030
0
            ret = azure_blob_store_file_delete(ctx, file);
2031
0
            if (ret != 0) {
2032
0
                flb_plg_error(ctx->ins, "cb_azure_blob_ingest :: failed to delete ingested file %s", fsf->name);
2033
0
                if (file) {
2034
0
                    azure_blob_store_file_unlock(file);
2035
0
                    file->failures += 1;
2036
0
                }
2037
0
            }
2038
2039
            /* Clean up resources */
2040
0
            flb_free(buffer);
2041
0
            flb_sds_destroy(payload);
2042
0
            flb_sds_destroy(tag_sds);
2043
0
            break;
2044
0
        }
2045
2046
        /* Ensure file is unlocked if max retries reached */
2047
0
        if (retry_count >= ctx->scheduler_max_retries) {
2048
0
            flb_plg_error(ctx->ins, "cb_azure_blob_ingest :: Max retries reached for file :: attempting to delete/marking inactive %s",
2049
0
                          file->fsf->name);
2050
0
            if (ctx->delete_on_max_upload_error){
2051
0
                azure_blob_store_file_delete(ctx, file);
2052
0
            }
2053
0
            else {
2054
0
                azure_blob_store_file_inactive(ctx, file);
2055
0
            }
2056
0
        }
2057
2058
0
        flb_plg_debug(ctx->ins, "Exited upload timer callback (cb_azure_blob_ingest)..");
2059
0
    }
2060
0
    pthread_mutex_unlock(&ctx->buffer_mutex);
2061
0
}
2062
2063
2064
static int ingest_all_chunks(struct flb_azure_blob *ctx, struct flb_config *config)
2065
0
{
2066
0
    struct azure_blob_file *chunk;
2067
0
    struct mk_list *tmp;
2068
0
    struct mk_list *head;
2069
0
    struct mk_list *f_head;
2070
0
    struct flb_fstore_file *fsf;
2071
0
    struct flb_fstore_stream *fs_stream;
2072
0
    flb_sds_t payload = NULL;
2073
0
    char *buffer = NULL;
2074
0
    size_t buffer_size;
2075
0
    int ret;
2076
0
    flb_sds_t tag_sds;
2077
2078
0
    mk_list_foreach(head, &ctx->fs->streams) {
2079
        /* skip multi upload stream */
2080
0
        fs_stream = mk_list_entry(head, struct flb_fstore_stream, _head);
2081
0
        if (fs_stream == ctx->stream_upload) {
2082
0
            continue;
2083
0
        }
2084
2085
0
        mk_list_foreach_safe(f_head, tmp, &fs_stream->files) {
2086
0
            fsf = mk_list_entry(f_head, struct flb_fstore_file, _head);
2087
0
            chunk = fsf->data;
2088
2089
            /* Locked chunks are being processed, skip */
2090
0
            if (chunk->locked == FLB_TRUE) {
2091
0
                continue;
2092
0
            }
2093
2094
0
            if (chunk->failures >= ctx->scheduler_max_retries) {
2095
0
                flb_plg_warn(ctx->ins,
2096
0
                             "ingest_all_chunks :: Chunk for tag %s failed to send %i times, "
2097
0
                             "will not retry",
2098
0
                             (char *) fsf->meta_buf, ctx->scheduler_max_retries);
2099
0
                if (ctx->delete_on_max_upload_error){
2100
0
                    azure_blob_store_file_delete(ctx, chunk);
2101
0
                }
2102
0
                else {
2103
0
                    azure_blob_store_file_inactive(ctx, chunk);
2104
0
                }
2105
0
                continue;
2106
0
            }
2107
2108
0
            ret = construct_request_buffer(ctx, NULL, chunk,
2109
0
                                           &buffer, &buffer_size);
2110
0
            if (ret < 0) {
2111
0
                flb_plg_error(ctx->ins,
2112
0
                              "ingest_all_chunks :: Could not construct request buffer for %s",
2113
0
                              chunk->file_path);
2114
0
                return -1;
2115
0
            }
2116
2117
0
            payload = flb_sds_create_len(buffer, buffer_size);
2118
0
            tag_sds = flb_sds_create(fsf->meta_buf);
2119
0
            flb_free(buffer);
2120
2121
0
            ret = send_blob(config, NULL, ctx, FLB_EVENT_TYPE_LOGS, ctx->btype, (char *)tag_sds, 0, (char *)tag_sds,
2122
0
                             flb_sds_len(tag_sds), NULL, payload, flb_sds_len(payload));
2123
2124
0
            if (ret != FLB_OK) {
2125
0
                flb_plg_error(ctx->ins, "ingest_all_chunks :: Failed to ingest data to Azure Blob Storage");
2126
0
                if (chunk){
2127
0
                    azure_blob_store_file_unlock(chunk);
2128
0
                    chunk->failures += 1;
2129
0
                }
2130
0
                flb_sds_destroy(tag_sds);
2131
0
                flb_sds_destroy(payload);
2132
0
                return -1;
2133
0
            }
2134
2135
0
            flb_sds_destroy(tag_sds);
2136
0
            flb_sds_destroy(payload);
2137
2138
            /* data was sent successfully- delete the local buffer */
2139
0
            azure_blob_store_file_cleanup(ctx, chunk);
2140
0
        }
2141
0
    }
2142
2143
0
    return 0;
2144
0
}
2145
2146
/*
2147
 * Drain startup buffers and create the upload timer under buffer_mutex.
2148
 * Return -1 on failure so the caller can unlock and request an engine retry.
2149
 */
2150
static int flush_init(void *out_context, struct flb_config *config)
2151
0
{
2152
0
    int ret;
2153
0
    struct flb_azure_blob *ctx = out_context;
2154
0
    struct flb_sched *sched;
2155
2156
    /* clean up any old buffers found on startup */
2157
0
    if (ctx->has_old_buffers == FLB_TRUE) {
2158
0
        flb_plg_info(ctx->ins,
2159
0
                     "Sending locally buffered data from previous "
2160
0
                     "executions to azure blob; buffer=%s",
2161
0
                     ctx->fs->root_path);
2162
0
        ctx->has_old_buffers = FLB_FALSE;
2163
0
        ret = ingest_all_chunks(ctx, config);
2164
0
        if (ret < 0) {
2165
0
            ctx->has_old_buffers = FLB_TRUE;
2166
0
            flb_plg_error(ctx->ins,
2167
0
                          "Failed to send locally buffered data left over "
2168
0
                          "from previous executions; will retry. Buffer=%s",
2169
0
                          ctx->fs->root_path);
2170
0
            return -1;
2171
0
        }
2172
0
    }
2173
0
    else {
2174
0
        flb_plg_debug(ctx->ins,
2175
0
                      "Did not find any local buffered data from previous "
2176
0
                      "executions to azure blob; buffer=%s",
2177
0
                      ctx->fs->root_path);
2178
0
    }
2179
2180
    /*
2181
    * create a timer that will run periodically and check if uploads
2182
    * are ready for completion
2183
    * this is created once on the first flush
2184
    */
2185
0
    if (ctx->timer_created == FLB_FALSE) {
2186
0
        flb_plg_debug(ctx->ins,
2187
0
                      "Creating upload timer with frequency %ds",
2188
0
                      ctx->timer_ms / 1000);
2189
2190
0
        sched = flb_sched_ctx_get();
2191
2192
0
        ret = flb_sched_timer_cb_create(sched, FLB_SCHED_TIMER_CB_PERM,
2193
0
                                        ctx->timer_ms, cb_azure_blob_ingest, ctx, NULL);
2194
0
        if (ret == -1) {
2195
0
            flb_plg_error(ctx->ins, "Failed to create upload timer");
2196
0
            return -1;
2197
0
        }
2198
0
        ctx->timer_created = FLB_TRUE;
2199
0
    }
2200
2201
0
    return 0;
2202
0
}
2203
2204
/*
2205
 * Flush a chunk to Azure or serialize its local buffering with buffer_mutex.
2206
 * Release the mutex on every buffered success and retry path.
2207
 */
2208
static void cb_azure_blob_flush(struct flb_event_chunk *event_chunk,
2209
                                struct flb_output_flush *out_flush,
2210
                                struct flb_input_instance *i_ins,
2211
                                void *out_context,
2212
                                struct flb_config *config)
2213
0
{
2214
0
    int ret = FLB_OK;
2215
0
    struct flb_azure_blob *ctx = out_context;
2216
0
    (void) i_ins;
2217
0
    (void) config;
2218
0
    flb_sds_t json = NULL;
2219
0
    size_t json_size;
2220
2221
0
    if (event_chunk->type == FLB_EVENT_TYPE_LOGS) {
2222
0
        if (ctx->buffering_enabled == FLB_TRUE) {
2223
0
            size_t tag_len;
2224
0
            struct azure_blob_file *upload_file = NULL;
2225
0
            int upload_timeout_check = FLB_FALSE;
2226
0
            int total_file_size_check = FLB_FALSE;
2227
2228
0
            char *final_payload = NULL;
2229
0
            size_t final_payload_size = 0;
2230
0
            flb_sds_t tag_name = NULL;
2231
2232
            /* Buffered requests use synchronous I/O while the store is locked. */
2233
0
            pthread_mutex_lock(&ctx->buffer_mutex);
2234
2235
0
            flb_plg_trace(ctx->ins, "flushing bytes for event tag %s and size %zu", event_chunk->tag, event_chunk->size);
2236
2237
0
            if (ctx->unify_tag == FLB_TRUE) {
2238
0
                tag_name = flb_sds_create("fluentbit-buffer-file-unify-tag.log");
2239
0
            }
2240
0
            else {
2241
0
                tag_name = event_chunk->tag;
2242
0
            }
2243
0
            if (!tag_name) {
2244
0
                goto error;
2245
0
            }
2246
0
            tag_len = flb_sds_len(tag_name);
2247
2248
0
            if (flush_init(ctx, config) != 0) {
2249
0
                goto error;
2250
0
            }
2251
            /* Reformat msgpack to JSON payload */
2252
0
            ret = azure_blob_format(config, i_ins, ctx, NULL, FLB_EVENT_TYPE_LOGS, tag_name, tag_len, event_chunk->data, event_chunk->size, (void **)&json, &json_size);
2253
0
            if (ret != 0) {
2254
0
                flb_plg_error(ctx->ins, "cannot reformat data into json");
2255
0
                goto error;
2256
0
            }
2257
2258
            /* Get a file candidate matching the given 'tag' */
2259
0
            upload_file = azure_blob_store_file_get(ctx, tag_name, tag_len);
2260
2261
            /* Handle upload timeout or file size limits */
2262
0
            if (upload_file != NULL) {
2263
0
                if (upload_file->failures >= ctx->scheduler_max_retries) {
2264
0
                    flb_plg_warn(ctx->ins, "File with tag %s failed to send %d times, will not retry", event_chunk->tag, ctx->scheduler_max_retries);
2265
0
                    if (ctx->delete_on_max_upload_error) {
2266
0
                        azure_blob_store_file_delete(ctx, upload_file);
2267
0
                    } else {
2268
0
                        azure_blob_store_file_inactive(ctx, upload_file);
2269
0
                    }
2270
0
                    upload_file = NULL;
2271
0
                } else if (time(NULL) > (upload_file->create_time + ctx->upload_timeout)) {
2272
0
                    upload_timeout_check = FLB_TRUE;
2273
0
                } else if (upload_file->size + json_size > ctx->file_size) {
2274
0
                    total_file_size_check = FLB_TRUE;
2275
0
                }
2276
0
            }
2277
2278
0
            if (upload_file != NULL && (upload_timeout_check == FLB_TRUE || total_file_size_check == FLB_TRUE)) {
2279
0
                flb_plg_debug(ctx->ins, "uploading file %s with size %zu", upload_file->fsf->name, upload_file->size);
2280
2281
                /* Construct the payload for upload */
2282
0
                ret = construct_request_buffer(ctx, json, upload_file, &final_payload, &final_payload_size);
2283
0
                if (ret != 0) {
2284
0
                    flb_plg_error(ctx->ins, "error constructing request buffer for %s", event_chunk->tag);
2285
0
                    upload_file->failures += 1;
2286
0
                    goto error;
2287
0
                }
2288
2289
                /*
2290
                * Azure blob requires a container. The following function validate that the container exists,
2291
                * otherwise it will be created. Note that that container name is specified by the user
2292
                * in the configuration file.
2293
                *
2294
                * https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-container-create#about-container-naming
2295
                */
2296
0
                ret = ensure_container(ctx);
2297
0
                if (ret == FLB_FALSE) {
2298
0
                    azure_blob_store_file_unlock(upload_file);
2299
0
                    goto error;
2300
0
                }
2301
2302
                /* Upload the file */
2303
0
                ret = send_blob(config, i_ins, ctx, FLB_EVENT_TYPE_LOGS, ctx->btype,(char *)tag_name, 0, (char *)tag_name,
2304
0
                                 tag_len, NULL, final_payload, final_payload_size);
2305
2306
0
                if (ret == FLB_OK) {
2307
0
                    flb_plg_debug(ctx->ins, "uploaded file %s successfully", upload_file->fsf->name);
2308
0
                    azure_blob_store_file_delete(ctx, upload_file);
2309
0
                    goto cleanup;
2310
0
                }
2311
0
                else {
2312
0
                    flb_plg_error(ctx->ins, "error uploading file %s", upload_file->fsf->name);
2313
0
                    if (upload_file) {
2314
0
                        azure_blob_store_file_unlock(upload_file);
2315
0
                        upload_file->failures += 1;
2316
0
                    }
2317
0
                    goto error;
2318
0
                }
2319
0
            }
2320
0
            else {
2321
                /* Buffer current chunk */
2322
0
                ret = azure_blob_store_buffer_put(ctx, upload_file, tag_name, tag_len, json, json_size);
2323
0
                if (ret == 0) {
2324
0
                    flb_plg_debug(ctx->ins, "buffered chunk %s", event_chunk->tag);
2325
0
                    goto cleanup;
2326
0
                }
2327
0
                else {
2328
0
                    flb_plg_error(ctx->ins, "failed to buffer chunk %s", event_chunk->tag);
2329
0
                    goto error;
2330
0
                }
2331
0
            }
2332
2333
0
            cleanup:
2334
0
            if (json) {
2335
0
                flb_sds_destroy(json);
2336
0
            }
2337
0
            if (tag_name && ctx->unify_tag == FLB_TRUE) {
2338
0
                flb_sds_destroy(tag_name);
2339
0
            }
2340
0
            if (final_payload) {
2341
0
                flb_free(final_payload);
2342
0
            }
2343
0
            pthread_mutex_unlock(&ctx->buffer_mutex);
2344
0
            FLB_OUTPUT_RETURN(FLB_OK);
2345
2346
0
            error:
2347
0
            if (json) {
2348
0
                flb_sds_destroy(json);
2349
0
            }
2350
0
            if (tag_name && ctx->unify_tag == FLB_TRUE) {
2351
0
                flb_sds_destroy(tag_name);
2352
0
            }
2353
0
            if (final_payload) {
2354
0
                flb_free(final_payload);
2355
0
            }
2356
0
            pthread_mutex_unlock(&ctx->buffer_mutex);
2357
0
            FLB_OUTPUT_RETURN(FLB_RETRY);
2358
0
        }
2359
0
        else {
2360
2361
            /*
2362
            * Azure blob requires a container. The following function validate that the container exists,
2363
            * otherwise it will be created. Note that that container name is specified by the user
2364
            * in the configuration file.
2365
            *
2366
            * https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-container-create#about-container-naming
2367
            */
2368
0
            ret = ensure_container(ctx);
2369
0
            if (ret == FLB_FALSE) {
2370
0
                FLB_OUTPUT_RETURN(FLB_RETRY);
2371
0
            }
2372
2373
0
            ret = azure_blob_format(config, i_ins, ctx, NULL, FLB_EVENT_TYPE_LOGS,(char *) event_chunk->tag, flb_sds_len(event_chunk->tag), (char *) event_chunk->data ,event_chunk->size, (void **)&json, &json_size);
2374
0
            if (ret != 0) {
2375
0
                flb_plg_error(ctx->ins, "cannot reformat data into json");
2376
0
                ret = FLB_RETRY;
2377
0
            }
2378
            /* Buffering mode is disabled, proceed with regular flow */
2379
0
            ret = send_blob(config, i_ins, ctx,
2380
0
                            FLB_EVENT_TYPE_LOGS,
2381
0
                            ctx->btype, /* blob type per user configuration  */
2382
0
                            (char *) event_chunk->tag,  /* use tag as 'name' */
2383
0
                            0,  /* part id */
2384
0
                            (char *) event_chunk->tag, flb_sds_len(event_chunk->tag),
2385
0
                            NULL,
2386
0
                            json, json_size);
2387
2388
            /* send_blob handles CREATE_BLOB internally */
2389
0
        }
2390
0
    }
2391
0
    else if (event_chunk->type == FLB_EVENT_TYPE_BLOBS) {
2392
        /*
2393
         * For Blob types, we use the flush callback to enqueue the file, then cb_azb_blob_file_upload()
2394
         * takes care of the rest like reading the file and uploading it to Azure.
2395
         */
2396
0
        ret = process_blob_chunk(ctx, event_chunk);
2397
0
        if (ret == -1) {
2398
0
            FLB_OUTPUT_RETURN(FLB_RETRY);
2399
0
        }
2400
0
    }
2401
2402
0
    if (json){
2403
0
        flb_sds_destroy(json);
2404
0
    }
2405
2406
    /* FLB_RETRY, FLB_OK, FLB_ERROR */
2407
0
    FLB_OUTPUT_RETURN(ret);
2408
0
}
2409
2410
/*
2411
 * Upload remaining buffers after workers have joined, then destroy the store
2412
 * and its mutex. A NULL context requires no cleanup.
2413
 */
2414
static int cb_azure_blob_exit(void *data, struct flb_config *config)
2415
0
{
2416
0
    struct flb_azure_blob *ctx = data;
2417
0
    int ret = -1;
2418
2419
0
    if (!ctx) {
2420
0
        return 0;
2421
0
    }
2422
2423
0
    if (ctx->buffering_enabled == FLB_TRUE){
2424
0
        if (azure_blob_store_has_data(ctx) == FLB_TRUE) {
2425
0
            flb_plg_info(ctx->ins, "Sending all locally buffered data to Azure Blob");
2426
0
            ret = ingest_all_chunks(ctx, config);
2427
0
            if (ret < 0) {
2428
0
                flb_plg_error(ctx->ins, "Could not send all chunks on exit");
2429
0
            }
2430
0
        }
2431
0
        azure_blob_store_exit(ctx);
2432
0
    }
2433
2434
0
    if (ctx->u) {
2435
0
        flb_upstream_destroy(ctx->u);
2436
0
        ctx->u = NULL;
2437
0
    }
2438
2439
0
    pthread_mutex_destroy(&ctx->buffer_mutex);
2440
0
    flb_azure_blob_conf_destroy(ctx);
2441
0
    return 0;
2442
0
}
2443
2444
/* worker initialization, used for our internal timers */
2445
static int cb_worker_init(void *data, struct flb_config *config)
2446
0
{
2447
0
    int ret;
2448
0
    struct worker_info *info;
2449
0
    struct flb_azure_blob *ctx = data;
2450
2451
0
    flb_plg_info(ctx->ins, "initializing worker");
2452
2453
0
    info = FLB_TLS_GET(worker_info);
2454
0
    if (!info) {
2455
        /* initialize worker global info */
2456
0
        info = flb_malloc(sizeof(struct worker_info));
2457
0
        if (!info) {
2458
0
            flb_errno();
2459
0
            return -1;
2460
0
        }
2461
0
        info->active_upload = FLB_FALSE;
2462
0
        FLB_TLS_SET(worker_info, info);
2463
0
    }
2464
2465
0
    ret = azb_timer_create(ctx);
2466
0
    if (ret == -1) {
2467
0
        flb_plg_error(ctx->ins, "failed to create upload timer");
2468
0
        return -1;
2469
0
    }
2470
2471
0
    return 0;
2472
0
}
2473
2474
/* worker teardown */
2475
static int cb_worker_exit(void *data, struct flb_config *config)
2476
0
{
2477
0
    struct worker_info *info;
2478
0
    struct flb_azure_blob *ctx = data;
2479
2480
0
    flb_plg_info(ctx->ins, "initializing worker");
2481
2482
0
    info = FLB_TLS_GET(worker_info);
2483
0
    if (info != NULL) {
2484
0
        flb_free(info);
2485
0
        FLB_TLS_SET(worker_info, NULL);
2486
0
    }
2487
2488
0
    return 0;
2489
0
}
2490
2491
/* Configuration properties map */
2492
static struct flb_config_map config_map[] = {
2493
    {
2494
     FLB_CONFIG_MAP_STR, "account_name", NULL,
2495
     0, FLB_TRUE, offsetof(struct flb_azure_blob, account_name),
2496
     "Azure account name (mandatory)"
2497
    },
2498
2499
    {
2500
     FLB_CONFIG_MAP_STR, "container_name", NULL,
2501
     0, FLB_TRUE, offsetof(struct flb_azure_blob, container_name),
2502
     "Container name (mandatory)"
2503
    },
2504
2505
    {
2506
     FLB_CONFIG_MAP_BOOL, "auto_create_container", "true",
2507
     0, FLB_TRUE, offsetof(struct flb_azure_blob, auto_create_container),
2508
     "Auto create container if it don't exists"
2509
    },
2510
2511
    {
2512
     FLB_CONFIG_MAP_STR, "blob_type", "appendblob",
2513
     0, FLB_TRUE, offsetof(struct flb_azure_blob, blob_type),
2514
     "Set the block type: appendblob or blockblob"
2515
    },
2516
2517
    {
2518
     FLB_CONFIG_MAP_STR, "compress", NULL,
2519
     0, FLB_FALSE, 0,
2520
        "Set payload compression in network transfer. Options: 'gzip', 'zstd'"
2521
    },
2522
2523
    {
2524
     FLB_CONFIG_MAP_BOOL, "compress_blob", "false",
2525
     0, FLB_TRUE, offsetof(struct flb_azure_blob, compress_blob),
2526
        "Enable block blob compression in the final blob file (defaults to gzip, "
2527
        "uses the 'compress' codec when set). This option is not compatible with "
2528
        "'appendblob' block type"
2529
    },
2530
2531
    {
2532
     FLB_CONFIG_MAP_BOOL, "emulator_mode", "false",
2533
     0, FLB_TRUE, offsetof(struct flb_azure_blob, emulator_mode),
2534
     "Use emulator mode, enable it if you want to use Azurite"
2535
    },
2536
2537
    {
2538
     FLB_CONFIG_MAP_STR, "shared_key", NULL,
2539
     0, FLB_TRUE, offsetof(struct flb_azure_blob, shared_key),
2540
     "Azure shared key"
2541
    },
2542
2543
    {
2544
     FLB_CONFIG_MAP_STR, "endpoint", NULL,
2545
     0, FLB_TRUE, offsetof(struct flb_azure_blob, endpoint),
2546
     "Custom full URL endpoint to use an emulator"
2547
    },
2548
2549
    {
2550
     FLB_CONFIG_MAP_STR, "path", NULL,
2551
     0, FLB_TRUE, offsetof(struct flb_azure_blob, path),
2552
     "Set a path for your blob"
2553
    },
2554
2555
    {
2556
     FLB_CONFIG_MAP_STR, "date_key", "@timestamp",
2557
     0, FLB_TRUE, offsetof(struct flb_azure_blob, date_key),
2558
     "Name of the key that will have the record timestamp"
2559
    },
2560
2561
    {
2562
     FLB_CONFIG_MAP_STR, "auth_type", "key",
2563
     0, FLB_TRUE, offsetof(struct flb_azure_blob, auth_type),
2564
     "Set the auth type: key or sas"
2565
    },
2566
2567
    {
2568
     FLB_CONFIG_MAP_STR, "sas_token", NULL,
2569
     0, FLB_TRUE, offsetof(struct flb_azure_blob, sas_token),
2570
     "Azure Blob SAS token"
2571
    },
2572
2573
    {
2574
     FLB_CONFIG_MAP_STR, "database_file", NULL,
2575
     0, FLB_TRUE, offsetof(struct flb_azure_blob, database_file),
2576
     "Absolute path to a database file to be used to store blob files contexts"
2577
    },
2578
2579
    {
2580
     FLB_CONFIG_MAP_SIZE, "part_size", "25M",
2581
     0, FLB_TRUE, offsetof(struct flb_azure_blob, part_size),
2582
     "Size of each part when uploading blob files"
2583
    },
2584
2585
    {
2586
     FLB_CONFIG_MAP_INT, "file_delivery_attempt_limit", "1",
2587
     0, FLB_TRUE, offsetof(struct flb_azure_blob, file_delivery_attempt_limit),
2588
     "File delivery attempt limit"
2589
    },
2590
2591
    {
2592
     FLB_CONFIG_MAP_INT, "part_delivery_attempt_limit", "1",
2593
     0, FLB_TRUE, offsetof(struct flb_azure_blob, part_delivery_attempt_limit),
2594
     "File part delivery attempt limit"
2595
    },
2596
2597
    {
2598
     FLB_CONFIG_MAP_TIME, "upload_parts_timeout", "10M",
2599
     0, FLB_TRUE, offsetof(struct flb_azure_blob, upload_parts_timeout),
2600
     "Timeout to upload parts of a blob file"
2601
    },
2602
2603
    {
2604
     FLB_CONFIG_MAP_TIME, "upload_part_freshness_limit", "6D",
2605
     0, FLB_TRUE, offsetof(struct flb_azure_blob, upload_parts_freshness_threshold),
2606
     "Maximum lifespan of an uncommitted file part"
2607
    },
2608
2609
    {
2610
     FLB_CONFIG_MAP_STR, "configuration_endpoint_url", NULL,
2611
     0, FLB_TRUE, offsetof(struct flb_azure_blob, configuration_endpoint_url),
2612
     "Configuration endpoint URL. With tls.verify enabled, tls.ca_file must "
2613
     "trust both the Blob and configuration endpoints when tls.ca_path is unset. "
2614
     "If tls.ca_path is set, it takes precedence over tls.ca_file."
2615
    },
2616
2617
    {
2618
     FLB_CONFIG_MAP_STR, "configuration_endpoint_username", NULL,
2619
     0, FLB_TRUE, offsetof(struct flb_azure_blob, configuration_endpoint_username),
2620
     "Configuration endpoint basic authentication username"
2621
    },
2622
2623
    {
2624
     FLB_CONFIG_MAP_STR, "configuration_endpoint_password", NULL,
2625
     0, FLB_TRUE, offsetof(struct flb_azure_blob, configuration_endpoint_password),
2626
     "Configuration endpoint basic authentication password"
2627
    },
2628
2629
    {
2630
     FLB_CONFIG_MAP_STR, "configuration_endpoint_bearer_token", NULL,
2631
     0, FLB_TRUE, offsetof(struct flb_azure_blob, configuration_endpoint_bearer_token),
2632
     "Configuration endpoint bearer token"
2633
    },
2634
2635
    {
2636
     FLB_CONFIG_MAP_BOOL, "buffering_enabled", "false",
2637
     0, FLB_TRUE, offsetof(struct flb_azure_blob, buffering_enabled),
2638
     "Enable buffering into disk before ingesting into Azure Blob"
2639
    },
2640
2641
    {
2642
     FLB_CONFIG_MAP_STR, "buffer_dir", "/tmp/fluent-bit/azure-blob/",
2643
     0, FLB_TRUE, offsetof(struct flb_azure_blob, buffer_dir),
2644
     "Specifies the location of directory where the buffered data will be stored"
2645
    },
2646
2647
    {
2648
     FLB_CONFIG_MAP_TIME, "upload_timeout", "30m",
2649
     0, FLB_TRUE, offsetof(struct flb_azure_blob, upload_timeout),
2650
     "Optionally specify a timeout for uploads. "
2651
           "Fluent Bit will start ingesting buffer files which have been created more than x minutes and haven't reached upload_file_size limit yet"
2652
           "Default is 30m."
2653
    },
2654
2655
    {
2656
     FLB_CONFIG_MAP_SIZE, "upload_file_size", "200M",
2657
     0, FLB_TRUE, offsetof(struct flb_azure_blob, file_size),
2658
     "Specifies the size of files to be uploaded in MBs. Default is 200MB"
2659
    },
2660
2661
    {
2662
     FLB_CONFIG_MAP_STR, "azure_blob_buffer_key", "key",
2663
     0, FLB_TRUE, offsetof(struct flb_azure_blob, azure_blob_buffer_key),
2664
     "Set the azure blob buffer key which needs to be specified when using multiple instances of azure blob output plugin and buffering is enabled"
2665
    },
2666
2667
    {
2668
     FLB_CONFIG_MAP_SIZE, "store_dir_limit_size", "8G",
2669
     0, FLB_TRUE, offsetof(struct flb_azure_blob, store_dir_limit_size),
2670
     "Set the max size of the buffer directory. Default is 8GB"
2671
    },
2672
2673
    {
2674
     FLB_CONFIG_MAP_BOOL, "buffer_file_delete_early", "false",
2675
     0, FLB_TRUE, offsetof(struct flb_azure_blob, buffer_file_delete_early),
2676
     "Whether to delete the buffered file early after successful blob creation. Default is false"
2677
    },
2678
2679
    { 
2680
     FLB_CONFIG_MAP_INT, "blob_uri_length", "64",
2681
     0, FLB_TRUE, offsetof(struct flb_azure_blob, blob_uri_length),
2682
     "Set the length of generated blob uri before ingesting to Azure Kusto. Default is 64"
2683
    },
2684
2685
    {
2686
     FLB_CONFIG_MAP_BOOL, "unify_tag", "false",
2687
     0, FLB_TRUE, offsetof(struct flb_azure_blob, unify_tag),
2688
     "Whether to create a single buffer file when buffering mode is enabled. Default is false"
2689
    },
2690
2691
    {
2692
     FLB_CONFIG_MAP_INT, "scheduler_max_retries", "3",
2693
     0, FLB_TRUE, offsetof(struct flb_azure_blob, scheduler_max_retries),
2694
     "Maximum number of retries for the scheduler send blob. Default is 3"
2695
    },
2696
2697
    {
2698
     FLB_CONFIG_MAP_BOOL, "delete_on_max_upload_error", "false",
2699
     0, FLB_TRUE, offsetof(struct flb_azure_blob, delete_on_max_upload_error),
2700
     "Whether to delete the buffer file on maximum upload errors. Default is false"
2701
    },
2702
2703
    {
2704
     FLB_CONFIG_MAP_TIME, "io_timeout", "60s",0, FLB_TRUE, offsetof(struct flb_azure_blob, io_timeout),
2705
     "Buffered HTTP I/O timeout when net.io_timeout is unset. Default is 60s"
2706
    },
2707
2708
    /* EOF */
2709
    {0}
2710
};
2711
2712
/* Plugin registration */
2713
struct flb_output_plugin out_azure_blob_plugin = {
2714
    .name           = "azure_blob",
2715
    .description    = "Azure Blob Storage",
2716
    .cb_init        = cb_azure_blob_init,
2717
    .cb_flush       = cb_azure_blob_flush,
2718
    .cb_exit        = cb_azure_blob_exit,
2719
    .cb_worker_init = cb_worker_init,
2720
    .cb_worker_exit = cb_worker_exit,
2721
2722
    /* Test */
2723
    .test_formatter.callback = azure_blob_format,
2724
2725
    .flags        = FLB_OUTPUT_NET | FLB_IO_OPT_TLS,
2726
    .event_type   = FLB_OUTPUT_LOGS | FLB_OUTPUT_BLOBS,
2727
    .config_map   = config_map,
2728
    .workers      = 1,
2729
};