diff --git a/CMakeLists.txt b/CMakeLists.txt index b0f43a96f48..ae853815b70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -255,6 +255,7 @@ option(FLB_OUT_PROMETHEUS_REMOTE_WRITE "Enable Prometheus remote write plugin" option(FLB_OUT_S3 "Enable AWS S3 output plugin" Yes) option(FLB_OUT_VIVO_EXPORTER "Enabel Vivo exporter output plugin" Yes) option(FLB_OUT_WEBSOCKET "Enable Websocket output plugin" Yes) +option(FLB_OUT_ORACLE_LOG_ANALYTICS "Enable Oracle Cloud Infrastructure Logging analytics plugin" Yes) option(FLB_OUT_CHRONICLE "Enable Google Chronicle output plugin" Yes) option(FLB_FILTER_ALTER_SIZE "Enable alter_size filter" Yes) option(FLB_FILTER_AWS "Enable aws filter" Yes) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 2715876119d..36299074ab2 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -304,6 +304,7 @@ REGISTER_OUT_PLUGIN("out_nats") REGISTER_OUT_PLUGIN("out_nrlogs") REGISTER_OUT_PLUGIN("out_null") REGISTER_OUT_PLUGIN("out_opensearch") +REGISTER_OUT_PLUGIN("out_oracle_log_analytics") if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows") REGISTER_OUT_PLUGIN("out_plot") diff --git a/plugins/out_oracle_log_analytics/CMakeLists.txt b/plugins/out_oracle_log_analytics/CMakeLists.txt new file mode 100644 index 00000000000..81f971a9501 --- /dev/null +++ b/plugins/out_oracle_log_analytics/CMakeLists.txt @@ -0,0 +1,6 @@ +set(src + oci_logan.c + oci_logan_conf.c + ) + +FLB_PLUGIN(out_oracle_log_analytics "${src}" "") diff --git a/plugins/out_oracle_log_analytics/oci_logan.c b/plugins/out_oracle_log_analytics/oci_logan.c new file mode 100644 index 00000000000..630812e2d28 --- /dev/null +++ b/plugins/out_oracle_log_analytics/oci_logan.c @@ -0,0 +1,1313 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2023 The Fluent Bit Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "oci_logan_conf.h" +#include "oci_logan.h" + + +static int check_config_from_record(msgpack_object key, + char *name, int len) +{ + if (key.type != MSGPACK_OBJECT_STR) { + return FLB_FALSE; + } + + if (key.via.str.size != len) { + return FLB_FALSE; + } + + + return memcmp(key.via.str.ptr, name, len) == 0; +} + +/* + * Authorization: Signature version="1",keyId="//", + * algorithm="rsa-sha256",headers="(request-target) date x-content-sha256 content-type content-length", + * signature="signature" + */ +static flb_sds_t create_authorization_header_content(struct flb_oci_logan *ctx, + flb_sds_t signature) +{ + flb_sds_t content; + + content = flb_sds_create_size(512); + flb_sds_cat_safe(&content, FLB_OCI_SIGN_SIGNATURE_VERSION, + sizeof(FLB_OCI_SIGN_SIGNATURE_VERSION) - 1); + flb_sds_cat_safe(&content, ",", 1); + flb_sds_cat_safe(&content, FLB_OCI_SIGN_KEYID, + sizeof(FLB_OCI_SIGN_KEYID) - 1); + flb_sds_cat_safe(&content, "=\"", 2); + flb_sds_cat_safe(&content, ctx->key_id, flb_sds_len(ctx->key_id)); + flb_sds_cat_safe(&content, "\",", 2); + flb_sds_cat_safe(&content, FLB_OCI_SIGN_ALGORITHM, + sizeof(FLB_OCI_SIGN_ALGORITHM) - 1); + flb_sds_cat_safe(&content, ",", 1); + flb_sds_cat_safe(&content, FLB_OCI_SIGN_HEADERS, + sizeof(FLB_OCI_SIGN_HEADERS) - 1); + flb_sds_cat_safe(&content, ",", 1); + flb_sds_cat_safe(&content, FLB_OCI_SIGN_SIGNATURE, + sizeof(FLB_OCI_SIGN_SIGNATURE) - 1); + flb_sds_cat_safe(&content, "=\"", 2); + flb_sds_cat_safe(&content, signature, flb_sds_len(signature)); + flb_sds_cat_safe(&content, "\"", 1); + + return content; +} + +static flb_sds_t create_base64_sha256_signature(struct flb_oci_logan *ctx, + flb_sds_t signing_string) +{ + int len = 0, ret; + size_t outlen; + flb_sds_t signature; + unsigned char sha256_buf[32] = { 0 }; + unsigned char sig[256] = { 0 }; + size_t sig_len = sizeof(sig); + + ret = flb_hash_simple(FLB_HASH_SHA256, + (unsigned char*) signing_string, + flb_sds_len(signing_string), + sha256_buf, sizeof(sha256_buf)); + + if(ret != FLB_CRYPTO_SUCCESS) { + flb_plg_error(ctx->ins, "error generating hash buffer"); + return NULL; + } + + ret = flb_crypto_sign_simple(FLB_CRYPTO_PRIVATE_KEY, + FLB_CRYPTO_PADDING_PKCS1, + FLB_HASH_SHA256, + (unsigned char *) ctx->private_key, + flb_sds_len(ctx->private_key), + sha256_buf, sizeof(sha256_buf), + sig, &sig_len); + + + if (ret != FLB_CRYPTO_SUCCESS) { + flb_plg_error(ctx->ins, "error signing SHA256"); + return NULL; + } + + signature = flb_sds_create_size(512); + if (!signature) { + flb_errno(); + return NULL; + } + + /* base 64 encode */ + len = flb_sds_alloc(signature) - 1; + flb_base64_encode((unsigned char*) signature, len, &outlen, sig, + sizeof(sig)); + signature[outlen] = '\0'; + flb_sds_len_set(signature, outlen); + + return signature; +} + +static flb_sds_t get_date(void) +{ + + flb_sds_t rfc1123date; + time_t t; + size_t size; + struct tm tm = { 0 }; + + /* Format Date */ + rfc1123date = flb_sds_create_size(32); + if (!rfc1123date) { + flb_errno(); + return NULL; + } + + t = time(NULL); + if (!gmtime_r(&t, &tm)) { + flb_errno(); + flb_sds_destroy(rfc1123date); + return NULL; + } + size = strftime(rfc1123date, flb_sds_alloc(rfc1123date) - 1, + "%a, %d %b %Y %H:%M:%S GMT", &tm); + if (size <= 0) { + flb_errno(); + flb_sds_destroy(rfc1123date); + return NULL; + } + flb_sds_len_set(rfc1123date, size); + return rfc1123date; +} + +static flb_sds_t add_header_and_signing(struct flb_http_client *c, + flb_sds_t signing_str, const char *header, int headersize, + const char *val, int val_size) +{ + if (!signing_str) { + return NULL; + } + + flb_http_add_header(c, header, headersize, val, val_size); + + flb_sds_cat_safe(&signing_str, "\n", 1); + flb_sds_cat_safe(&signing_str, header, headersize); + flb_sds_cat_safe(&signing_str, ": ", 2); + flb_sds_cat_safe(&signing_str, val, val_size); + + return signing_str; +} + +static int build_headers(struct flb_http_client *c, struct flb_oci_logan *ctx, + flb_sds_t json, flb_sds_t hostname, int port, flb_sds_t uri) +{ + int ret = -1; + flb_sds_t tmp_sds = NULL; + flb_sds_t signing_str = NULL; + flb_sds_t rfc1123date = NULL; + flb_sds_t encoded_uri = NULL; + flb_sds_t signature = NULL; + flb_sds_t auth_header_str = NULL; + + flb_sds_t tmp_ref = NULL; + + size_t tmp_len = 0; + + unsigned char sha256_buf[32] = { 0 }; + + tmp_sds = flb_sds_create_size(512); + if (!tmp_sds) { + flb_errno(); + goto error_label; + } + + signing_str = flb_sds_create_size(1024); + if (!signing_str) { + flb_errno(); + goto error_label; + } + + /* Add (requeset-target) to signing string */ + encoded_uri = flb_uri_encode(uri, flb_sds_len(uri)); + if (!encoded_uri) { + flb_errno(); + goto error_label; + } + flb_sds_cat_safe(&signing_str, FLB_OCI_HEADER_REQUEST_TARGET, + sizeof(FLB_OCI_HEADER_REQUEST_TARGET) - 1); + flb_sds_cat_safe(&signing_str, ": post ", sizeof(": post ") - 1); + flb_sds_cat_safe(&signing_str, encoded_uri, + flb_sds_len(encoded_uri)); + + /* Add Host to Header */ + if (((c->flags & FLB_IO_TLS) && c->port == 443) + || (!(c->flags & FLB_IO_TLS) && c->port == 80)) { + /* default port */ + tmp_ref = flb_sds_copy(tmp_sds, c->host, strlen(c->host)); + } + else { + tmp_ref = flb_sds_printf(&tmp_sds, "%s:%i", c->host, c->port); + } + if (!tmp_ref) { + flb_plg_error(ctx->ins, "cannot compose temporary host header"); + goto error_label; + } + tmp_sds = tmp_ref; + tmp_ref = NULL; + + signing_str = add_header_and_signing(c, signing_str, FLB_OCI_HEADER_HOST, + sizeof(FLB_OCI_HEADER_HOST) - 1, + tmp_sds, flb_sds_len(tmp_sds)); + if (!signing_str) { + flb_plg_error(ctx->ins, "cannot compose signing string"); + goto error_label; + } + + /* Add Date header */ + rfc1123date = get_date(); + if (!rfc1123date) { + flb_plg_error(ctx->ins, "cannot compose temporary date header"); + goto error_label; + } + signing_str = add_header_and_signing(c, signing_str, FLB_OCI_HEADER_DATE, + sizeof(FLB_OCI_HEADER_DATE) - 1, rfc1123date, + flb_sds_len(rfc1123date)); + if (!signing_str) { + flb_plg_error(ctx->ins, "cannot compose signing string"); + goto error_label; + } + + /* Add x-content-sha256 Header */ + ret = flb_hash_simple(FLB_HASH_SHA256, + (unsigned char*) json, + flb_sds_len(json), + sha256_buf, sizeof(sha256_buf)); + + if (ret != FLB_CRYPTO_SUCCESS) { + flb_plg_error(ctx->ins, "error forming hash buffer for x-content-sha256 Header"); + goto error_label; + } + + flb_base64_encode((unsigned char*) tmp_sds, flb_sds_len(tmp_sds) - 1, + &tmp_len, sha256_buf, sizeof(sha256_buf)); + + tmp_sds[tmp_len] = '\0'; + flb_sds_len_set(tmp_sds, tmp_len); + + signing_str = add_header_and_signing(c, signing_str, + FLB_OCI_HEADER_X_CONTENT_SHA256, + sizeof(FLB_OCI_HEADER_X_CONTENT_SHA256) - 1, tmp_sds, + flb_sds_len(tmp_sds)); + if (!signing_str) { + flb_plg_error(ctx->ins, "cannot compose signing string"); + goto error_label; + } + + /* Add content-Type */ + signing_str = add_header_and_signing(c, signing_str, + FLB_OCI_HEADER_CONTENT_TYPE, sizeof(FLB_OCI_HEADER_CONTENT_TYPE) - 1, + FLB_OCI_HEADER_CONTENT_TYPE_VAL, + sizeof(FLB_OCI_HEADER_CONTENT_TYPE_VAL) - 1); + if (!signing_str) { + flb_plg_error(ctx->ins, "cannot compose signing string"); + goto error_label; + } + + /* Add content-Length */ + tmp_len = snprintf(tmp_sds, flb_sds_alloc(tmp_sds) - 1, "%i", + (int) flb_sds_len(json)); + flb_sds_len_set(tmp_sds, tmp_len); + signing_str = add_header_and_signing(c, signing_str, + FLB_OCI_HEADER_CONTENT_LENGTH, sizeof(FLB_OCI_HEADER_CONTENT_LENGTH) - 1, + tmp_sds, flb_sds_len(tmp_sds)); + if (!signing_str) { + flb_plg_error(ctx->ins, "cannot compose signing string"); + goto error_label; + } + + /* Add Authorization header */ + signature = create_base64_sha256_signature(ctx, signing_str); + if (!signature) { + flb_plg_error(ctx->ins, "cannot compose signing signature"); + goto error_label; + } + + auth_header_str = create_authorization_header_content(ctx, signature); + if (!auth_header_str) { + flb_plg_error(ctx->ins, "cannot compose authorization header"); + goto error_label; + } + + flb_http_add_header(c, FLB_OCI_HEADER_AUTH, sizeof(FLB_OCI_HEADER_AUTH) - 1, + auth_header_str, flb_sds_len(auth_header_str)); + + /* User-Agent */ + flb_http_add_header(c, FLB_OCI_HEADER_USER_AGENT, + sizeof(FLB_OCI_HEADER_USER_AGENT) - 1, + FLB_OCI_HEADER_USER_AGENT_VAL, + sizeof(FLB_OCI_HEADER_USER_AGENT_VAL) - 1); + + /* Accept */ + flb_http_add_header(c, "Accept", 6, "*/*", 3); + + ret = 0; + + error_label: + if (tmp_sds) { + flb_sds_destroy(tmp_sds); + } + if (signing_str) { + flb_sds_destroy(signing_str); + } + if (rfc1123date) { + flb_sds_destroy(rfc1123date); + } + if (encoded_uri) { + flb_sds_destroy(encoded_uri); + } + if (signature) { + flb_sds_destroy(signature); + } + if (auth_header_str) { + flb_sds_destroy(auth_header_str); + } + return ret; +} + +static struct flb_oci_error_response* parse_response_error(struct flb_oci_logan *ctx, + char *response, size_t response_len) +{ + int tok_size = 32, ret, i; + jsmn_parser parser; + jsmntok_t *t; + jsmntok_t *tokens; + char *key; + char *val; + int key_len; + int val_len; + struct flb_oci_error_response *error_response; + + jsmn_init(&parser); + + tokens = flb_calloc(1, sizeof(jsmntok_t) * tok_size); + if (!tokens) { + flb_errno(); + return NULL; + } + + ret = jsmn_parse(&parser, response, response_len, tokens, tok_size); + + if (ret == JSMN_ERROR_INVAL || ret == JSMN_ERROR_PART) { + flb_free(tokens); + flb_plg_info(ctx->ins, + "Unable to parser error response. reponse is not valid json"); + return NULL; + } + tok_size = ret; + + error_response = flb_calloc(1, sizeof(struct flb_oci_error_response)); + if (!error_response) { + flb_errno(); + flb_free(tokens); + return NULL; + } + + /* Parse JSON tokens */ + for (i = 0; i < tok_size; i++) { + t = &tokens[i]; + + if (t->start == -1 || t->end == -1 || (t->start == 0 && t->end == 0)) { + break; + } + + if (t->type != JSMN_STRING) { + continue; + } + + key = response + t->start; + key_len = (t->end - t->start); + + i++; + t = &tokens[i]; + val = response + t->start; + val_len = (t->end - t->start); + + if (val_len < 1) { + continue; + } + + if ((key_len == sizeof(FLB_OCI_ERROR_RESPONSE_CODE) - 1) + && strncasecmp(key, FLB_OCI_ERROR_RESPONSE_CODE, + sizeof(FLB_OCI_ERROR_RESPONSE_CODE) - 1) == 0) { + /* code */ + error_response->code = flb_sds_create_len(val, val_len); + if (!error_response->code) { + flb_free(error_response); + flb_free(tokens); + return NULL; + } + } + else if ((key_len == sizeof(FLB_OCI_ERROR_RESPONSE_MESSAGE) - 1) + && strncasecmp(key, FLB_OCI_ERROR_RESPONSE_MESSAGE, + sizeof(FLB_OCI_ERROR_RESPONSE_MESSAGE) - 1) == 0) { + + /* message */ + error_response->message = flb_sds_create_len(val, val_len); + if (!error_response->message) { + flb_free(error_response); + flb_free(tokens); + return NULL; + } + } + } + + flb_free(tokens); + return error_response; +} + +static int retry_error(struct flb_http_client *c, struct flb_oci_logan *ctx) +{ + struct flb_oci_error_response *error_response = NULL; + int tmp_len; + int ret = FLB_FALSE; + + /* possible retry error message */ + if ( !(c->resp.status == 400 || c->resp.status == 401 + || c->resp.status == 404 || c->resp.status == 409 + || c->resp.status == 429 || c->resp.status == 500)) { + return FLB_FALSE; + } + + /* parse error message */ + error_response = parse_response_error(ctx, c->resp.payload, + c->resp.payload_size); + if (!error_response) { + return FLB_FALSE; + } + + if (error_response->code) { + tmp_len = flb_sds_len(error_response->code); + if (c->resp.status == 400 + && (tmp_len == sizeof(FLB_OCI_ERROR_CODE_RELATED_RESOURCE_NOT_FOUND) - 1) + && strncasecmp(error_response->code, FLB_OCI_ERROR_CODE_RELATED_RESOURCE_NOT_FOUND, tmp_len) == 0) { + ret = FLB_TRUE; + } + else if( c->resp.status == 401 + &&( tmp_len == sizeof(FLB_OCI_ERROR_CODE_NOT_AUTHENTICATED)-1 ) + && strncasecmp(error_response->code, FLB_OCI_ERROR_CODE_NOT_AUTHENTICATED, tmp_len) == 0) { + ret = FLB_TRUE; + } + else if (c->resp.status == 404 + && (tmp_len == sizeof(FLB_OCI_ERROR_CODE_NOT_AUTHENTICATEDORNOTFOUND) - 1) + && strncasecmp(error_response->code, FLB_OCI_ERROR_CODE_NOT_AUTHENTICATEDORNOTFOUND, tmp_len) == 0) { + ret = FLB_TRUE; + } + else if (c->resp.status == 409 + && (tmp_len == sizeof(FLB_OCI_ERROR_CODE_INCORRECTSTATE) - 1) + && strncasecmp(error_response->code, FLB_OCI_ERROR_CODE_INCORRECTSTATE, tmp_len) == 0) { + ret = FLB_TRUE; + } + else if (c->resp.status == 409 + && (tmp_len == sizeof(FLB_OCI_ERROR_CODE_NOT_AUTH_OR_RESOURCE_EXIST) - 1) + && strncasecmp(error_response->code, FLB_OCI_ERROR_CODE_NOT_AUTH_OR_RESOURCE_EXIST, tmp_len) == 0) { + ret = FLB_TRUE; + } + else if (c->resp.status == 429 + && (tmp_len == sizeof(FLB_OCI_ERROR_CODE_TOO_MANY_REQUESTS) - 1) + && strncasecmp(error_response->code, FLB_OCI_ERROR_CODE_TOO_MANY_REQUESTS, tmp_len) == 0) { + ret = FLB_TRUE; + } + else if (c->resp.status == 500 + && (tmp_len == sizeof(FLB_OCI_ERROR_CODE_INTERNAL_SERVER_ERROR) - 1) + && strncasecmp(error_response->code, FLB_OCI_ERROR_CODE_INTERNAL_SERVER_ERROR, tmp_len) == 0) { + ret = FLB_TRUE; + } + } + + if (error_response->code) { + flb_sds_destroy(error_response->code); + } + if (error_response->message) { + flb_sds_destroy(error_response->message); + } + flb_free(error_response); + + return ret; +} + +static int cb_oci_logan_init(struct flb_output_instance *ins, + struct flb_config *config, + void *data) +{ + struct flb_oci_logan *ctx; + ctx = flb_oci_logan_conf_create(ins, config); + if (!ctx) { + flb_plg_error(ins, "cannot initialize plugin"); + return -1; + } + flb_plg_info(ins, "initialized logan plugin"); + flb_output_set_context(ins, ctx); + flb_output_set_http_debug_callbacks(ins); + + return 0; +} + +static flb_sds_t compose_uri(struct flb_oci_logan *ctx, + flb_sds_t log_set, flb_sds_t log_group_id) +{ + flb_sds_t uri_param; + flb_sds_t full_uri; + + uri_param = flb_sds_create_size(512); + if (!uri_param) { + flb_errno(); + return NULL; + } + + /* LogGroupId */ + if (log_group_id) { + if (flb_sds_len(uri_param) > 0) { + flb_sds_cat_safe(&uri_param, "&", 1); + } + flb_sds_cat_safe(&uri_param, FLB_OCI_LOG_GROUP_ID, + FLB_OCI_LOG_GROUP_ID_SIZE); + flb_sds_cat_safe(&uri_param, "=", 1); + flb_sds_cat_safe(&uri_param, log_group_id, + flb_sds_len(log_group_id)); + } + + if (!uri_param) { + return NULL; + } + + /* logSet */ + if (log_set) { + if (flb_sds_len(uri_param) > 0) { + flb_sds_cat_safe(&uri_param, "&", 1); + } + flb_sds_cat_safe(&uri_param, FLB_OCI_LOG_SET, + FLB_OCI_LOG_SET_SIZE); + flb_sds_cat_safe(&uri_param, "=", 1); + flb_sds_cat_safe(&uri_param, log_set, + flb_sds_len(log_set)); + } + + if (!uri_param) { + return NULL; + } + + flb_sds_cat_safe(&uri_param, "&", 1); + flb_sds_cat_safe(&uri_param, FLB_OCI_PAYLOAD_TYPE, + sizeof(FLB_OCI_PAYLOAD_TYPE) - 1); + flb_sds_cat_safe(&uri_param, "=", 1); + flb_sds_cat_safe(&uri_param, "JSON", 4); + + + if (!uri_param) { + return NULL; + } + + + if (flb_sds_len(uri_param) == 0) { + flb_sds_destroy(uri_param); + return flb_sds_create(ctx->uri); + } + + full_uri = flb_sds_create_size( + flb_sds_len(ctx->uri) + 1 + flb_sds_len(uri_param)); + if (!full_uri) { + flb_errno(); + flb_sds_destroy(uri_param); + return NULL; + } + + flb_sds_cat_safe(&full_uri, ctx->uri, flb_sds_len(ctx->uri)); + flb_sds_cat_safe(&full_uri, "?", 1); + flb_sds_cat_safe(&full_uri, uri_param, flb_sds_len(uri_param)); + + flb_sds_destroy(uri_param); + + return full_uri; +} + +static int flush_to_endpoint(struct flb_oci_logan *ctx, + flb_sds_t payload, + flb_sds_t log_group_id, + flb_sds_t log_set_id) +{ + int out_ret = FLB_RETRY; + int http_ret; + size_t b_sent; + flb_sds_t full_uri; + struct flb_http_client *c = NULL; + struct flb_connection *u_conn; + + full_uri = compose_uri(ctx, log_set_id, log_group_id); + if(!full_uri) { + flb_plg_error(ctx->ins, "unable to compose uri for logGroup: %s logSet: %s", + ctx->oci_la_log_group_id, ctx->oci_la_log_set_id); + } + + flb_plg_debug(ctx->ins, "full_uri=%s", full_uri); + + u_conn = flb_upstream_conn_get(ctx->u); + if(!u_conn) { + goto error_label; + } + /* Create HTTP client context */ + c = flb_http_client(u_conn, FLB_HTTP_POST, full_uri, (void*) payload, + flb_sds_len(payload), ctx->ins->host.name, ctx->ins->host.port, ctx->proxy, 0); + if (!c) { + goto error_label; + } + flb_http_allow_duplicated_headers(c, FLB_FALSE); + + flb_plg_debug(ctx->ins, "built client"); + flb_http_buffer_size(c, FLB_HTTP_DATA_SIZE_MAX); + if (build_headers(c, ctx, payload, ctx->ins->host.name, ctx->ins->host.port, full_uri) < 0) { + flb_plg_error(ctx->ins, "failed to build headers"); + goto error_label; + } + flb_plg_debug(ctx->ins, "built request"); + + out_ret = FLB_OK; + + http_ret = flb_http_do(c, &b_sent); + flb_plg_debug(ctx->ins, "placed request"); + + if (http_ret == 0) { + + if (c->resp.status != 200) { + flb_plg_debug(ctx->ins, "request header %s", c->header_buf); + + out_ret = FLB_ERROR; + + if (c->resp.payload && c->resp.payload_size > 0) { + if (retry_error(c, ctx) == FLB_TRUE) { + out_ret = FLB_RETRY; + } + + flb_plg_error(ctx->ins, "%s:%i, retry=%s, HTTP status=%i\n%s", + ctx->ins->host.name, ctx->ins->host.port, + (out_ret == FLB_RETRY ? "true" : "false"), + c->resp.status, c->resp.payload); + } + else { + flb_plg_error(ctx->ins, "%s:%i, retry=%s, HTTP status=%i", + ctx->ins->host.name, ctx->ins->host.port, + (out_ret == FLB_RETRY ? "true" : "false"), + c->resp.status); + } + } + } + else { + out_ret = FLB_RETRY; + flb_plg_error(ctx->ins, "could not flush records to %s:%i (http_do=%i), retry=%s", + ctx->ins->host.name, ctx->ins->host.port, + http_ret, (out_ret == FLB_RETRY ? "true" : "false")); + goto error_label; + } + + + + error_label: + if (full_uri) { + flb_sds_destroy(full_uri); + } + + /* Destroy HTTP client context */ + if (c) { + flb_http_client_destroy(c); + } + + /* Release the TCP connection */ + if (u_conn) { + flb_upstream_conn_release(u_conn); + } + + return out_ret; + +} + +static void pack_oci_fields(msgpack_packer *packer, + struct flb_oci_logan *ctx) +{ + int num_global_meta = 0; + int num_event_meta = 0; + int pck_sz = 2; + struct mk_list *head = NULL; + struct metadata_obj *f; + + + /* number of meta properties */ + if(ctx->oci_la_global_metadata != NULL) { + num_global_meta = mk_list_size(&ctx->global_metadata_fields); + } + if(ctx->oci_la_metadata != NULL) { + num_event_meta = mk_list_size(&ctx->log_event_metadata_fields); + } + + + if (num_global_meta > 0) { + msgpack_pack_map(packer, 2); + msgpack_pack_str(packer, FLB_OCI_LOG_METADATA_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_METADATA, + FLB_OCI_LOG_METADATA_SIZE); + + msgpack_pack_map(packer, num_global_meta); + /* pack kv list */ + mk_list_foreach(head, &ctx->global_metadata_fields) { + f = mk_list_entry(head, struct metadata_obj, _head); + + msgpack_pack_str(packer, flb_sds_len(f->key)); + msgpack_pack_str_body(packer, f->key, flb_sds_len(f->key)); + + msgpack_pack_str(packer, flb_sds_len(f->val)); + msgpack_pack_str_body(packer, f->val, flb_sds_len(f->val)); + + } + + } + else { + msgpack_pack_map(packer, 1); + } + + /* + *logEvents":[ + { + "entityId":"", + "logSourceName":"LinuxSyslogSource", + "logPath":"/var/log/messages", + "metadata":{ + "Error ID":"1", + "Environment":"prod", + "Client Host Region":"PST" + }, + "logRecords":[ + "May 8 2017 04:02:36 blr00akm syslogd 1.4.1: shutdown.", + "May 8 2017 04:02:37 blr00akm syslogd 1.4.1: restart." + ] + }, + { + + } + ] + */ + msgpack_pack_str(packer, FLB_OCI_LOG_EVENTS_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_EVENTS, FLB_OCI_LOG_EVENTS_SIZE); + + msgpack_pack_array(packer, 1); + + if (ctx->oci_la_entity_id) { + pck_sz++; + } + if (ctx->oci_la_log_path) { + pck_sz++; + } + if (ctx->oci_la_entity_type) { + pck_sz++; + } + + if (num_event_meta > 0) { + pck_sz++; + } + + msgpack_pack_map(packer, pck_sz); /* entityId, logSourceName, logPath, logRecords */ + + + /* "entityType:"" */ + if (ctx->oci_la_entity_type) { + msgpack_pack_str(packer, FLB_OCI_ENTITY_TYPE_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_ENTITY_TYPE, FLB_OCI_ENTITY_TYPE_SIZE); + msgpack_pack_str(packer, flb_sds_len(ctx->oci_la_entity_type)); + msgpack_pack_str_body(packer, ctx->oci_la_entity_type, + flb_sds_len(ctx->oci_la_entity_type)); + } + + /* "entityId":"", */ + if (ctx->oci_la_entity_id) { + msgpack_pack_str(packer, FLB_OCI_ENTITY_ID_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_ENTITY_ID, FLB_OCI_ENTITY_ID_SIZE); + msgpack_pack_str(packer, flb_sds_len(ctx->oci_la_entity_id)); + msgpack_pack_str_body(packer, ctx->oci_la_entity_id, + flb_sds_len(ctx->oci_la_entity_id)); + } + + + /* "logSourceName":"", */ + msgpack_pack_str(packer, FLB_OCI_LOG_SOURCE_NAME_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_SOURCE_NAME, + FLB_OCI_LOG_SOURCE_NAME_SIZE); + msgpack_pack_str(packer, flb_sds_len(ctx->oci_la_log_source_name)); + msgpack_pack_str_body(packer, ctx->oci_la_log_source_name, + flb_sds_len(ctx->oci_la_log_source_name)); + + + /* "logPath":"" */ + if (ctx->oci_la_log_path) { + msgpack_pack_str(packer, FLB_OCI_LOG_PATH_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_PATH, FLB_OCI_LOG_PATH_SIZE); + msgpack_pack_str(packer, flb_sds_len(ctx->oci_la_log_path)); + msgpack_pack_str_body(packer, ctx->oci_la_log_path, + flb_sds_len(ctx->oci_la_log_path)); + } + + + /* Add metadata */ + if (num_event_meta > 0) { + /* + "metadata":{ + "Error ID":"0", + "Environment":"dev", + "Client Host Region":"IST" + }, + */ + msgpack_pack_str(packer, FLB_OCI_LOG_METADATA_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_METADATA, + FLB_OCI_LOG_METADATA_SIZE); + + msgpack_pack_map(packer, num_event_meta); + /* pack kv list */ + mk_list_foreach(head, &ctx->log_event_metadata_fields) { + f = mk_list_entry(head, struct metadata_obj, _head); + + msgpack_pack_str(packer, flb_sds_len(f->key)); + msgpack_pack_str_body(packer, f->key, flb_sds_len(f->key)); + + msgpack_pack_str(packer, flb_sds_len(f->val)); + msgpack_pack_str_body(packer, f->val, flb_sds_len(f->val)); + + } + + } +} + +static int get_and_pack_oci_fields_from_record(msgpack_packer *packer, + msgpack_object map, + flb_sds_t *lg_id, + flb_sds_t *ls_id, + struct flb_oci_logan *ctx) +{ + int map_size = map.via.map.size; + int pck_size = 1, i; + msgpack_object *log_group_id= NULL; + msgpack_object *log_set_id = NULL; + msgpack_object *entity_id = NULL; + msgpack_object *entity_type = NULL; + msgpack_object *log_path = NULL; + msgpack_object *log_source = NULL; + msgpack_object *global_metadata = NULL; + msgpack_object *metadata = NULL; + + for(i = 0; i < map_size; i++) { + if (check_config_from_record(map.via.map.ptr[i].key, + FLB_OCI_LOG_GROUP_ID_KEY, + FLB_OCI_LOG_GROUP_ID_KEY_SIZE) == FLB_TRUE) { + if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { + log_group_id = &map.via.map.ptr[i].val; + } + continue; + } + else if (check_config_from_record(map.via.map.ptr[i].key, + FLB_OCI_LOG_SET_ID_KEY, + FLB_OCI_LOG_SET_ID_KEY_SIZE) == FLB_TRUE) { + if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { + log_set_id = &map.via.map.ptr[i].val; + } + continue; + } + else if (check_config_from_record(map.via.map.ptr[i].key, + FLB_OCI_LOG_ENTITY_ID_KEY, + FLB_OCI_LOG_ENTITY_ID_KEY_SIZE) == FLB_TRUE) { + if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { + entity_id = &map.via.map.ptr[i].val; + pck_size++; + } + continue; + } + else if (check_config_from_record(map.via.map.ptr[i].key, + FLB_OCI_LOG_ENTITY_TYPE_KEY, + FLB_OCI_LOG_ENTITY_TYPE_KEY_SIZE) == FLB_TRUE) { + if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { + entity_type = &map.via.map.ptr[i].val; + pck_size++; + } + continue; + } + else if (check_config_from_record(map.via.map.ptr[i].key, + FLB_OCI_LOG_SOURCE_NAME_KEY, + FLB_OCI_LOG_SOURCE_NAME_KEY_SIZE) == FLB_TRUE) { + if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { + log_source = &map.via.map.ptr[i].val; + pck_size++; + } + continue; + } + else if (check_config_from_record(map.via.map.ptr[i].key, + FLB_OCI_LOG_PATH_KEY, + FLB_OCI_LOG_PATH_KEY_SIZE) == FLB_TRUE) { + if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { + log_path = &map.via.map.ptr[i].val; + pck_size++; + } + continue; + } + else if (check_config_from_record(map.via.map.ptr[i].key, + FLB_OCI_METADATA_KEY, + FLB_OCI_METADATA_KEY_SIZE) == FLB_TRUE) { + if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { + metadata = &map.via.map.ptr[i].val; + pck_size++; + } + continue; + } + else if (check_config_from_record(map.via.map.ptr[i].key, + FLB_OCI_GLOBAL_METADATA_KEY, + FLB_OCI_GLOBAL_METADATA_KEY_SIZE) == FLB_TRUE) { + if (map.via.map.ptr[i].val.type == MSGPACK_OBJECT_STR) { + global_metadata = &map.via.map.ptr[i].val; + } + continue; + } + } + + if (log_group_id == NULL || log_source == NULL) { + flb_plg_error(ctx->ins, + "log source name and log group id are required"); + return -1; + } + if (global_metadata != NULL) { + msgpack_pack_map(packer, 2); + msgpack_pack_str(packer, FLB_OCI_LOG_METADATA_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_METADATA, + FLB_OCI_LOG_METADATA_SIZE); + + msgpack_pack_object(packer, *global_metadata); + } + else { + msgpack_pack_map(packer, 1); + } + + /* + *logEvents":[ + { + "entityId":"", + "logSourceName":"LinuxSyslogSource", + "logPath":"/var/log/messages", + "metadata":{ + "Error ID":"1", + "Environment":"prod", + "Client Host Region":"PST" + }, + "logRecords":[ + "May 8 2017 04:02:36 blr00akm syslogd 1.4.1: shutdown.", + "May 8 2017 04:02:37 blr00akm syslogd 1.4.1: restart." + ] + }, + { + + } + ] + */ + msgpack_pack_str(packer, FLB_OCI_LOG_EVENTS_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_EVENTS, FLB_OCI_LOG_EVENTS_SIZE); + + msgpack_pack_array(packer, 1); + + if (metadata != NULL) { + pck_size++; + msgpack_pack_map(packer, pck_size); /* entityType, entityId, logSourceName, logPath, metadata, logRecords */ + msgpack_pack_str(packer, FLB_OCI_LOG_METADATA_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_METADATA, + FLB_OCI_LOG_METADATA_SIZE); + msgpack_pack_object(packer, *global_metadata); + + } + else { + msgpack_pack_map(packer, pck_size); /* entityType, entityId, logSourceName, logPath, logRecords */ + } + + /* "entityType:"" */ + if (entity_type) { + msgpack_pack_str(packer, FLB_OCI_ENTITY_TYPE_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_ENTITY_TYPE, FLB_OCI_ENTITY_TYPE_SIZE); + msgpack_pack_object(packer, *entity_type); + } + + /* "entityId":"", */ + if (entity_type) { + msgpack_pack_str(packer, FLB_OCI_ENTITY_ID_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_ENTITY_ID, FLB_OCI_ENTITY_ID_SIZE); + msgpack_pack_object(packer, *entity_id); + } + + + + /* "logSourceName":"", */ + msgpack_pack_str(packer, FLB_OCI_LOG_SOURCE_NAME_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_SOURCE_NAME, + FLB_OCI_LOG_SOURCE_NAME_SIZE); + msgpack_pack_object(packer, *log_source); + + + /* "logPath":"" */ + if (log_path) { + msgpack_pack_str(packer, FLB_OCI_LOG_PATH_SIZE); + msgpack_pack_str_body(packer, FLB_OCI_LOG_PATH, FLB_OCI_LOG_PATH_SIZE); + msgpack_pack_object(packer, *log_path); + } + + *lg_id = flb_sds_create_len(log_group_id->via.str.ptr, log_group_id->via.str.size); + if(!*lg_id) { + return -1; + } + if (log_set_id != NULL) { + *ls_id = flb_sds_create_len(log_set_id->via.str.ptr, log_set_id->via.str.size); + if(!*ls_id) { + return -1; + } + } + return 0; + +} + +static int total_flush(struct flb_event_chunk *event_chunk, + struct flb_output_flush *out_flush, + struct flb_input_instance *ins, void *out_context, + struct flb_config *config) +{ + struct flb_oci_logan *ctx = out_context; + flb_sds_t out_buf = NULL; + int ret = 0, res = FLB_OK, ret1 = 0, i; + msgpack_object map; + int map_size; + msgpack_sbuffer mp_sbuf; + msgpack_packer mp_pck; + int msg = -1, log = -1; + struct flb_log_event_decoder log_decoder; + struct flb_log_event log_event; + int num_records; + flb_sds_t log_group_id = NULL; + flb_sds_t log_set_id = NULL; + int count = 0; + + ret = flb_log_event_decoder_init(&log_decoder, (char *) event_chunk->data, event_chunk->size); + + if (ret != FLB_EVENT_DECODER_SUCCESS) { + flb_plg_error(ctx->ins, + "Log event decoder initialization error : %d", ret); + res = FLB_ERROR; + goto clean_up; + } + + /* Create temporary msgpack buffer */ + msgpack_sbuffer_init(&mp_sbuf); + msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write); + + /* pack oci fields */ + /* pack_oci_fields(&mp_pck, ctx); */ + + num_records = flb_mp_count(event_chunk->data, event_chunk->size); + + while ((ret = flb_log_event_decoder_next( + &log_decoder, + &log_event)) == FLB_EVENT_DECODER_SUCCESS) { + map = *log_event.body; + map_size = map.via.map.size; + if (count < 1) { + if (ctx->oci_config_in_record == FLB_FALSE) { + pack_oci_fields(&mp_pck, ctx); + log_group_id = ctx->oci_la_log_group_id; + log_set_id = ctx->oci_la_log_set_id; + } else { + ret1 = get_and_pack_oci_fields_from_record(&mp_pck, map, &log_group_id, &log_set_id, ctx); + if (ret1 != 0) { + break; + } + } + msgpack_pack_str(&mp_pck, FLB_OCI_LOG_RECORDS_SIZE); + msgpack_pack_str_body(&mp_pck, FLB_OCI_LOG_RECORDS, + FLB_OCI_LOG_RECORDS_SIZE); + msgpack_pack_array(&mp_pck, num_records); + count++; + } + + for(i = 0; i < map_size; i++) { + if (check_config_from_record(map.via.map.ptr[i].key, + "message", + 7) == FLB_TRUE) { + msg = i; + } + if (check_config_from_record(map.via.map.ptr[i].key, + "log", + 3) == FLB_TRUE) { + log = i; + } + } + if (log >= 0) { + msgpack_pack_str(&mp_pck, map.via.map.ptr[log].val.via.str.size); + msgpack_pack_str_body(&mp_pck, map.via.map.ptr[log].val.via.str.ptr, + map.via.map.ptr[log].val.via.str.size); + } + else if (msg >= 0) { + msgpack_pack_str(&mp_pck, map.via.map.ptr[msg].val.via.str.size); + msgpack_pack_str_body(&mp_pck, map.via.map.ptr[msg].val.via.str.ptr, + map.via.map.ptr[msg].val.via.str.size); + } + log = -1; + msg = -1; + } + + if (ret1 != 0) { + res = FLB_ERROR; + msgpack_sbuffer_destroy(&mp_sbuf); + flb_log_event_decoder_destroy(&log_decoder); + goto clean_up; + } + + out_buf = flb_msgpack_raw_to_json_sds(mp_sbuf.data, mp_sbuf.size); + msgpack_sbuffer_destroy(&mp_sbuf); + flb_log_event_decoder_destroy(&log_decoder); + + flb_plg_debug(ctx->ins, "payload=%s", out_buf); + flb_plg_debug(ctx->ins, "lg_id=%s", log_group_id); + + ret = flush_to_endpoint(ctx, out_buf, log_group_id, log_set_id); + if(ret != FLB_OK) { + res = FLB_RETRY; + goto clean_up; + } + + clean_up: + if (out_buf != NULL) { + flb_sds_destroy(out_buf); + } + if (log_group_id != NULL && ctx->oci_config_in_record) { + flb_sds_destroy(log_group_id); + } + if (log_set_id != NULL && ctx->oci_config_in_record) { + flb_sds_destroy(log_set_id); + } + return res; +} + +static void cb_oci_logan_flush(struct flb_event_chunk *event_chunk, + struct flb_output_flush *out_flush, + struct flb_input_instance *ins, void *out_context, + struct flb_config *config) +{ + struct flb_oci_logan *ctx = out_context; + int ret = -1; + + ret = total_flush(event_chunk, out_flush, + ins, out_context, + config); + if (ret != FLB_OK) { + flb_oci_logan_conf_destroy(ctx); + FLB_OUTPUT_RETURN(ret); + } + flb_plg_debug(ctx->ins, "success"); + + FLB_OUTPUT_RETURN(FLB_OK); + +} + +static int cb_oci_logan_exit(void *data, struct flb_config *config) +{ + struct flb_oci_logan *ctx = data; + + flb_oci_logan_conf_destroy(ctx); + return 0; +} + +/* Configuration properties map */ +static struct flb_config_map config_map[] = { + { + FLB_CONFIG_MAP_STR, "config_file_location", "", + 0, FLB_TRUE, offsetof(struct flb_oci_logan, config_file_location), + "Location of the oci config file for user api key signing" + }, + { + FLB_CONFIG_MAP_STR, "profile_name", "DEFAULT", + 0, FLB_TRUE, offsetof(struct flb_oci_logan, profile_name), + "name of the profile in the config file from which the user configs should be loaded" + }, + { + FLB_CONFIG_MAP_BOOL, "oci_config_in_record", "false", + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_config_in_record), + "If true, oci_la_* configs will be read from the record" + }, + { + FLB_CONFIG_MAP_STR, "uri", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, uri), + "Set the uri for rest api request" + }, + { + FLB_CONFIG_MAP_STR, "oci_la_log_group_id", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_group_id), + "log group id" + }, + { + FLB_CONFIG_MAP_STR, "oci_la_log_set_id", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_set_id), + "" + }, + { + FLB_CONFIG_MAP_STR, "oci_la_entity_id", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_entity_id), + "" + }, + { + FLB_CONFIG_MAP_STR, "oci_la_entity_type", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_entity_type), + "" + }, + { + FLB_CONFIG_MAP_STR, "oci_la_log_source_name", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_source_name), + "" + }, + { + FLB_CONFIG_MAP_STR, "oci_la_log_set_id", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_set_id), + "" + }, + { + FLB_CONFIG_MAP_STR, "oci_la_log_path", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_log_path), + "" + }, + { + FLB_CONFIG_MAP_SLIST_2, "oci_la_global_metadata", NULL, + FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_global_metadata), + "" + }, + { + FLB_CONFIG_MAP_SLIST_2, "oci_la_metadata", NULL, + FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct flb_oci_logan, oci_la_metadata), + "" + }, + { + FLB_CONFIG_MAP_STR, "namespace", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, namespace), + "namespace in your tenancy where the log objects reside" + }, + { + FLB_CONFIG_MAP_STR, "proxy", NULL, + 0, FLB_TRUE, offsetof(struct flb_oci_logan, proxy), + "define proxy if required, in http://host:port format, supports only http protocol" + }, + + {0} +}; + +/* Plugin reference */ +struct flb_output_plugin out_oracle_log_analytics_plugin = { + .name = "oracle_log_analytics", + .description = "Oracle log analytics", + .cb_init = cb_oci_logan_init, + .cb_pre_run = NULL, + .cb_flush = cb_oci_logan_flush, + .cb_exit = cb_oci_logan_exit, + + /* Configuration */ + .config_map = config_map, + + /* Events supported */ + .event_type = FLB_OUTPUT_LOGS, + + + /* Plugin flags */ + .flags = FLB_OUTPUT_NET | FLB_IO_OPT_TLS, + .workers = 1, +}; diff --git a/plugins/out_oracle_log_analytics/oci_logan.h b/plugins/out_oracle_log_analytics/oci_logan.h new file mode 100644 index 00000000000..7cc9e75f4a1 --- /dev/null +++ b/plugins/out_oracle_log_analytics/oci_logan.h @@ -0,0 +1,215 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2023 The Fluent Bit Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef FLB_OUT_OCI_LOGAN_H +#define FLB_OUT_OCI_LOGAN_H + +#define FLB_OCI_LOG_ENTITY_ID_KEY "oci_la_entity_id" +#define FLB_OCI_LOG_ENTITY_ID_KEY_SIZE sizeof(FLB_OCI_LOG_ENTITY_ID_KEY) - 1 + +#define FLB_OCI_LOG_ENTITY_TYPE_KEY "oci_la_entity_type" +#define FLB_OCI_LOG_ENTITY_TYPE_KEY_SIZE sizeof(FLB_OCI_LOG_ENTITY_TYPE_KEY) - 1 + +#define FLB_OCI_LOG_GROUP_ID_KEY "oci_la_log_group_id" +#define FLB_OCI_LOG_GROUP_ID_KEY_SIZE sizeof(FLB_OCI_LOG_GROUP_ID_KEY) - 1 + +#define FLB_OCI_LOG_SET_ID_KEY "oci_la_log_set_id" +#define FLB_OCI_LOG_SET_ID_KEY_SIZE sizeof(FLB_OCI_LOG_SET_ID_KEY) - 1 + +#define FLB_OCI_LOG_SOURCE_NAME_KEY "oci_la_log_source_name" +#define FLB_OCI_LOG_SOURCE_NAME_KEY_SIZE sizeof(FLB_OCI_LOG_SOURCE_NAME_KEY) - 1 + +#define FLB_OCI_LOG_PATH_KEY "oci_la_log_path" +#define FLB_OCI_LOG_PATH_KEY_SIZE sizeof(FLB_OCI_LOG_PATH_KEY) - 1 + +#define FLB_OCI_METADATA_KEY "oci_la_metadata" +#define FLB_OCI_METADATA_KEY_SIZE sizeof(FLB_OCI_METADATA_KEY) - 1 + +#define FLB_OCI_GLOBAL_METADATA_KEY "oci_la_global_metadata" +#define FLB_OCI_GLOBAL_METADATA_KEY_SIZE sizeof(FLB_OCI_GLOBAL_METADATA_KEY) - 1 + +#define FLB_OCI_LOG_EVENTS "logEvents" +#define FLB_OCI_LOG_EVENTS_SIZE sizeof(FLB_OCI_LOG_EVENTS)-1 + +#define FLB_OCI_LOG_RECORDS "logRecords" +#define FLB_OCI_LOG_RECORDS_SIZE sizeof(FLB_OCI_LOG_RECORDS)-1 + +#define FLB_OCI_LOG_GROUP_ID "logGroupId" +#define FLB_OCI_LOG_GROUP_ID_SIZE sizeof(FLB_OCI_LOG_GROUP_ID)-1 + +#define FLB_OCI_ENTITY_TYPE "entityType" +#define FLB_OCI_ENTITY_TYPE_SIZE sizeof(FLB_OCI_ENTITY_TYPE) - 1 + +#define FLB_OCI_LOG_SET "logSet" +#define FLB_OCI_LOG_SET_SIZE sizeof(FLB_OCI_LOG_SET)-1 + +#define FLB_OCI_LOG_METADATA "metadata" +#define FLB_OCI_LOG_METADATA_SIZE sizeof(FLB_OCI_LOG_METADATA)-1 + +#define FLB_OCI_ENTITY_ID "entityId" +#define FLB_OCI_ENTITY_ID_SIZE sizeof(FLB_OCI_ENTITY_ID)-1 + +#define FLB_OCI_LOG_SOURCE_NAME "logSourceName" +#define FLB_OCI_LOG_SOURCE_NAME_SIZE sizeof(FLB_OCI_LOG_SOURCE_NAME)-1 + +#define FLB_OCI_LOG_PATH "logPath" +#define FLB_OCI_LOG_PATH_SIZE sizeof(FLB_OCI_LOG_PATH)-1 + +#define FLB_OCI_META_PREFIX "metadata_" +#define FLB_OCI_META_PREFIX_SIZE sizeof(FLB_OCI_META_PREFIX)-1 + +#define FLB_OCI_MATCH_PREFIX "oci_match_" +#define FLB_OCI_MATCH_PREFIX_SIZE sizeof(FLB_OCI_MATCH_PREFIX)-1 + +#ifdef FLB_HAVE_REGEX +#define FLB_OCI_MATCH_REGEX_PREFIX "oci_match_regex_" +#define FLB_OCI_MATCH_REGEX_PREFIX_SIZE sizeof(FLB_OCI_MATCH_REGEX_PREFIX)-1 +#endif + +/* Params */ +#define FLB_OCI_PARAM_SKIP_HTTP_POST "skip_http_post" +#define FLB_OCI_PARAM_URI "uri" +#define FLB_OCI_PARAM_ENABLE_TRACE_OUTPUT "enable_trace" +#define FLB_OCI_PARAM_TRACE_OUTPUT_PATH "trace_file_path" +#define FLB_OCI_PARAM_TRACE_OUTPUT_FILE "trace_file_name" +#define FLB_OCI_PARAM_COLLECT_TIME_FIELD "collect_time_field_name" + +#define FLB_OCI_PARAM_USE_RAW_RECORD "use_raw_record" +#define FLB_OCI_PARAM_USE_RAW_RECORD_SIZE sizeof(FLB_OCI_PARAM_USE_RAW_RECORD)-1 + +#define FLB_OCI_PARAM_INCLUDE_COLLECT_TIME "include_collect_time" +#define FLB_OCI_PARAM_INCLUDE_COLLECT_TIME_SIZE sizeof(FLB_OCI_PARAM_INCLUDE_COLLECT_TIME)-1 + +#define FLB_OCI_MATCH_ID_MAX 1000 // TO avoid too large memory allocation + +#define FLB_OCI_DEFAULT_COLLECT_TIME "oci_collect_time" +#define FLB_OCI_DEFAULT_COLLECT_TIME_SIZE sizeof(FLB_OCI_DEFAULT_COLLECT_TIME)-1 + +/* Http Header */ +#define FLB_OCI_HEADER_REQUEST_TARGET "(request-target)" +#define FLB_OCI_HEADER_USER_AGENT "User-Agent" +#define FLB_OCI_HEADER_USER_AGENT_VAL "Fluent-Bit" +#define FLB_OCI_HEADER_CONTENT_TYPE "content-type" +#define FLB_OCI_HEADER_CONTENT_TYPE_VAL "application/octet-stream" +#define FLB_OCI_HEADER_X_CONTENT_SHA256 "x-content-sha256" +#define FLB_OCI_HEADER_CONTENT_LENGTH "content-length" +#define FLB_OCI_HEADER_HOST "host" +#define FLB_OCI_HEADER_DATE "date" +#define FLB_OCI_HEADER_AUTH "Authorization" +#define FLB_OCI_PAYLOAD_TYPE "payloadType" + + +/* For OCI signing */ +#define FLB_OCI_PARAM_TENANCY "tenancy" +#define FLB_OCI_PARAM_USER "user" +#define FLB_OCI_PARAM_KEY_FINGERPRINT "fingerprint" +#define FLB_OCI_PARAM_KEY_FILE "key_file" +#define FLB_OCI_PARAM_REGION "region" +#define FLB_OCI_PARAM_KEY_FILE_PASSPHRASE "key_file_passphrase" + +#define FLB_OCI_SIGN_SIGNATURE_VERSION "Signature version=\"1\"" +#define FLB_OCI_SIGN_KEYID "keyId" +#define FLB_OCI_SIGN_ALGORITHM "algorithm=\"rsa-sha256\"" + +#define FLB_OCI_SIGN_HEADERS "headers=\"" \ + FLB_OCI_HEADER_REQUEST_TARGET " " \ + FLB_OCI_HEADER_HOST " " \ + FLB_OCI_HEADER_DATE " " \ + FLB_OCI_HEADER_X_CONTENT_SHA256 " " \ + FLB_OCI_HEADER_CONTENT_TYPE " " \ + FLB_OCI_HEADER_CONTENT_LENGTH "\"" + +#define FLB_OCI_SIGN_SIGNATURE "signature" + +/* For error response */ +#define FLB_OCI_ERROR_RESPONSE_CODE "code" +#define FLB_OCI_ERROR_RESPONSE_MESSAGE "message" + +#define FLB_OCI_ERROR_CODE_RELATED_RESOURCE_NOT_FOUND "RelatedResourceNotAuthorizedOrNotFound" +#define FLB_OCI_ERROR_CODE_NOT_AUTHENTICATED "NotAuthenticated" +#define FLB_OCI_ERROR_CODE_NOT_AUTHENTICATEDORNOTFOUND "NotAuthorizedOrNotFound" +#define FLB_OCI_ERROR_CODE_INCORRECTSTATE "IncorrectState" +#define FLB_OCI_ERROR_CODE_NOT_AUTH_OR_RESOURCE_EXIST "NotAuthorizedOrResourceAlreadyExists" +#define FLB_OCI_ERROR_CODE_TOO_MANY_REQUESTS "TooManyRequests" +#define FLB_OCI_ERROR_CODE_INTERNAL_SERVER_ERROR "InternalServerError" + +#include +#include +#include +#include +#include + +struct metadata_obj { + flb_sds_t key; + flb_sds_t val; + struct mk_list _head; + +}; + +struct flb_oci_error_response +{ + flb_sds_t code; + flb_sds_t message; +}; + +struct flb_oci_logan { + flb_sds_t namespace; + flb_sds_t config_file_location; + flb_sds_t profile_name; + int oci_config_in_record; + flb_sds_t uri; + + struct flb_upstream *u; + flb_sds_t proxy; + char *proxy_host; + int proxy_port; + + // oci_la_* configs + flb_sds_t oci_la_entity_id; + + flb_sds_t oci_la_entity_type; + + flb_sds_t oci_la_log_source_name; + + flb_sds_t oci_la_log_path; + + flb_sds_t oci_la_log_group_id; + + flb_sds_t oci_la_log_set_id; + + struct mk_list *oci_la_global_metadata; + struct mk_list global_metadata_fields; + struct mk_list *oci_la_metadata; + struct mk_list log_event_metadata_fields; + + // config_file + flb_sds_t user; + flb_sds_t region; + flb_sds_t tenancy; + flb_sds_t key_fingerprint; + flb_sds_t key_file; + /* For OCI signing */ + flb_sds_t key_id; // tenancy/user/key_fingerprint + flb_sds_t private_key; + + struct flb_output_instance *ins; + +}; +#endif diff --git a/plugins/out_oracle_log_analytics/oci_logan_conf.c b/plugins/out_oracle_log_analytics/oci_logan_conf.c new file mode 100644 index 00000000000..a3980318465 --- /dev/null +++ b/plugins/out_oracle_log_analytics/oci_logan_conf.c @@ -0,0 +1,493 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2023 The Fluent Bit Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "oci_logan.h" +#include "oci_logan_conf.h" + +static int create_pk_context(flb_sds_t filepath, const char *key_passphrase, + struct flb_oci_logan *ctx) +{ + int ret; + struct stat st; + struct file_info finfo; + FILE *fp; + flb_sds_t kbuffer; + + + ret = stat(filepath, &st); + if (ret == -1) { + flb_errno(); + flb_plg_error(ctx->ins, "cannot open key file %s", filepath); + return -1; + } + + if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { + flb_plg_error(ctx->ins, "key file is not a valid file: %s", filepath); + return -1; + } + + /* Read file content */ + if (mk_file_get_info(filepath, &finfo, MK_FILE_READ) != 0) { + flb_plg_error(ctx->ins, "error to read key file: %s", filepath); + return -1; + } + + if (!(fp = fopen(filepath, "rb"))) { + flb_plg_error(ctx->ins, "error to open key file: %s", filepath); + return -1; + } + + kbuffer = flb_sds_create_size(finfo.size + 1); + if (!kbuffer) { + flb_errno(); + fclose(fp); + return -1; + } + + ret = fread(kbuffer, finfo.size, 1, fp); + if (ret < 1) { + flb_sds_destroy(kbuffer); + fclose(fp); + flb_plg_error(ctx->ins, "fail to read key file: %s", filepath); + return -1; + } + fclose(fp); + + /* In mbedtls, for PEM, the buffer must contains a null-terminated string */ + kbuffer[finfo.size] = '\0'; + flb_sds_len_set(kbuffer, finfo.size + 1); + + ctx->private_key = kbuffer; + + return 0; +} + +static int load_oci_credentials(struct flb_oci_logan *ctx) +{ + flb_sds_t content; + int found_profile = 0, res = 0; + char *line, *profile = NULL; + int eq_pos = 0; + char* key = NULL; + char* val; + + content = flb_file_read(ctx->config_file_location); + if (content == NULL || flb_sds_len(content) == 0) + { + return -1; + } + flb_plg_debug(ctx->ins, "content = %s", content); + line = strtok(content, "\n"); + while(line != NULL) { + /* process line */ + flb_plg_debug(ctx->ins, "line = %s", line); + if(!found_profile && line[0] == '[') { + profile = mk_string_copy_substr(line, 1, strlen(line) - 1); + if(!strcmp(profile, ctx->profile_name)) { + flb_plg_info(ctx->ins, "found profile"); + found_profile = 1; + goto iterate; + } + mk_mem_free(profile); + } + if(found_profile) { + if(line[0] == '[') { + break; + } + eq_pos = mk_string_char_search(line, '=', strlen(line)); + flb_plg_debug(ctx->ins, "eq_pos %d", eq_pos); + key = mk_string_copy_substr(line, 0, eq_pos); + flb_plg_debug(ctx->ins, "key = %s", key); + val = line + eq_pos + 1; + if (!key || !val) { + res = -1; + break; + } + if (strcmp(key, FLB_OCI_PARAM_USER) == 0) { + ctx->user = flb_sds_create(val); + } + else if (strcmp(key, FLB_OCI_PARAM_TENANCY) == 0) { + ctx->tenancy = flb_sds_create(val); + } + else if (strcmp(key, FLB_OCI_PARAM_KEY_FILE) == 0) { + ctx->key_file = flb_sds_create(val); + } + else if (strcmp(key, FLB_OCI_PARAM_KEY_FINGERPRINT) == 0) { + ctx->key_fingerprint = flb_sds_create(val); + } + else if (strcmp(key, FLB_OCI_PARAM_REGION) == 0) { + ctx->region = flb_sds_create(val); + } + else { + goto iterate; + } + } + iterate: + if (profile) { + mk_mem_free(profile); + profile = NULL; + } + if (key) { + mk_mem_free(key); + key = NULL; + } + line = strtok(NULL, "\n"); + } + if (!found_profile) { + flb_errno(); + res = -1; + } + + flb_sds_destroy(content); + if (profile) { + mk_mem_free(profile); + } + if (key) { + mk_mem_free(key); + } + return res; +} + +static int global_metadata_fields_create(struct flb_oci_logan *ctx) +{ + struct mk_list *head; + struct flb_slist_entry *kname; + struct flb_slist_entry *val; + struct flb_config_map_val *mv; + struct metadata_obj *f; + + if (!ctx->oci_la_global_metadata) { + return 0; + } + + flb_config_map_foreach(head, mv, ctx->oci_la_global_metadata) { + kname = mk_list_entry_first(mv->val.list, struct flb_slist_entry, _head); + val = mk_list_entry_last(mv->val.list, struct flb_slist_entry, _head); + + f = flb_malloc(sizeof(struct metadata_obj)); + if (!f) { + flb_errno(); + return -1; + } + + f->key = flb_sds_create(kname->str); + if (!f->key) { + flb_free(f); + return -1; + } + f->val = flb_sds_create(val->str); + if (!f->val) { + flb_free(f); + return -1; + } + + + mk_list_add(&f->_head, &ctx->global_metadata_fields); + } + + return 0; +} + +static int log_event_metadata_create(struct flb_oci_logan *ctx) +{ + struct mk_list *head; + struct flb_slist_entry *kname; + struct flb_slist_entry *val; + struct flb_config_map_val *mv; + struct metadata_obj *f; + + if (!ctx->oci_la_metadata) { + return 0; + } + + flb_config_map_foreach(head, mv, ctx->oci_la_metadata) { + kname = mk_list_entry_first(mv->val.list, struct flb_slist_entry, _head); + val = mk_list_entry_last(mv->val.list, struct flb_slist_entry, _head); + + f = flb_malloc(sizeof(struct metadata_obj)); + if (!f) { + flb_errno(); + return -1; + } + + f->key = flb_sds_create(kname->str); + if (!f->key) { + flb_free(f); + return -1; + } + f->val = flb_sds_create(val->str); + if (!f->val) { + flb_free(f); + return -1; + } + + + mk_list_add(&f->_head, &ctx->log_event_metadata_fields); + } + + return 0; +} +struct flb_oci_logan *flb_oci_logan_conf_create(struct flb_output_instance *ins, + struct flb_config *config) { + struct flb_oci_logan *ctx; + struct flb_upstream *upstream; + flb_sds_t host = NULL; + int io_flags = 0, default_port; + const char *tmp; + int ret = 0; + char *protocol = NULL; + char *p_host = NULL; + char *p_port = NULL; + char *p_uri = NULL; + + ctx = flb_calloc(1, sizeof(struct flb_oci_logan)); + if (!ctx) { + flb_errno(); + return NULL; + } + + ctx->ins = ins; + + ret = flb_output_config_map_set(ins, (void *) ctx); + if (ret == -1) { + flb_plg_error(ctx->ins, "configuration error"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + + if (ctx->oci_config_in_record == FLB_FALSE) { + if (ctx->oci_la_log_source_name == NULL || + ctx->oci_la_log_group_id == NULL) { + flb_errno(); + flb_plg_error(ctx->ins, + "log source name and log group id are required"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + } + if (ctx->oci_la_global_metadata != NULL) { + mk_list_init(&ctx->global_metadata_fields); + ret = global_metadata_fields_create(ctx); + if (ret != 0) { + flb_errno(); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + } + + if (ctx->oci_la_metadata != NULL) { + mk_list_init(&ctx->log_event_metadata_fields); + ret = log_event_metadata_create(ctx); + if (ret != 0) { + flb_errno(); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + } + + if (!ctx->config_file_location) { + flb_errno(); + flb_plg_error(ctx->ins, "config file location is required"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + + ret = load_oci_credentials(ctx); + if(ret != 0) { + flb_errno(); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + + if (ins->host.name) { + host = ins->host.name; + } + else { + if (!ctx->region) { + flb_errno(); + flb_plg_error(ctx->ins, "Region is required"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + host = flb_sds_create_size(512); + flb_sds_snprintf(&host, flb_sds_alloc(host), "loganalytics.%s.oci.oraclecloud.com", ctx->region); + } + + if (!ctx->uri) { + if (!ctx->namespace) { + flb_errno(); + flb_plg_error(ctx->ins, "Namespace is required"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + ctx->uri = flb_sds_create_size(512); + flb_sds_snprintf(&ctx->uri, flb_sds_alloc(ctx->uri), + "/20200601/namespaces/%s/actions/uploadLogEventsFile", + ctx->namespace); + } + + + + if (create_pk_context(ctx->key_file, NULL, ctx) < 0) { + flb_plg_error(ctx->ins, "failed to create pk context"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + + + ctx->key_id = flb_sds_create_size(512); + flb_sds_snprintf(&ctx->key_id, flb_sds_alloc(ctx->key_id), + "%s/%s/%s", ctx->tenancy, ctx->user, ctx->key_fingerprint); + + + /* Check if SSL/TLS is enabled */ + io_flags = FLB_IO_TCP; + default_port = 80; + +#ifdef FLB_HAVE_TLS + if (ins->use_tls == FLB_TRUE) { + io_flags = FLB_IO_TLS; + default_port = 443; + } +#endif + + if (ins->host.ipv6 == FLB_TRUE) { + io_flags |= FLB_IO_IPV6; + } + + flb_output_net_default(host, default_port, ins); + flb_sds_destroy(host); + + if (ctx->proxy) { + ret = flb_utils_url_split(tmp, &protocol, &p_host, &p_port, &p_uri); + if (ret == -1) { + flb_plg_error(ctx->ins, "could not parse proxy parameter: '%s'", tmp); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + + ctx->proxy_host = p_host; + ctx->proxy_port = atoi(p_port); + flb_free(protocol); + flb_free(p_port); + flb_free(p_uri); + flb_free(p_host); + } + + if (ctx->proxy) { + upstream = flb_upstream_create(config, ctx->proxy_host, ctx->proxy_port, + io_flags, ins->tls); + } + else { + /* Prepare an upstream handler */ + upstream = flb_upstream_create(config, ins->host.name, ins->host.port, + io_flags, ins->tls); + } + + if (!upstream) { + flb_plg_error(ctx->ins, "cannot create Upstream context"); + flb_oci_logan_conf_destroy(ctx); + return NULL; + } + ctx->u = upstream; + + /* Set instance flags into upstream */ + flb_output_upstream_set(ctx->u, ins); + + return ctx; +} + +static void metadata_fields_destroy(struct flb_oci_logan *ctx) +{ + struct mk_list *tmp; + struct mk_list *head; + struct metadata_obj *f; + + mk_list_foreach_safe(head, tmp, &ctx->global_metadata_fields) { + f = mk_list_entry(head, struct metadata_obj, _head); + flb_sds_destroy(f->key); + flb_sds_destroy(f->val); + mk_list_del(&f->_head); + flb_free(f); + } + + mk_list_foreach_safe(head, tmp, &ctx->log_event_metadata_fields) { + f = mk_list_entry(head, struct metadata_obj, _head); + flb_sds_destroy(f->key); + flb_sds_destroy(f->val); + mk_list_del(&f->_head); + flb_free(f); + } + +} + +int flb_oci_logan_conf_destroy(struct flb_oci_logan *ctx) { + if(ctx == NULL) { + return 0; + } + + if (ctx->private_key) { + flb_sds_destroy(ctx->private_key); + } + if (ctx->uri) { + flb_sds_destroy(ctx->uri); + } + if (ctx->key_id) { + flb_sds_destroy(ctx->key_id); + } + if (ctx->key_file) { + flb_sds_destroy(ctx->key_file); + } + if(ctx->user) { + flb_sds_destroy(ctx->user); + } + if(ctx->key_fingerprint) { + flb_sds_destroy(ctx->key_fingerprint); + } + if(ctx->tenancy) { + flb_sds_destroy(ctx->tenancy); + } + if(ctx->region) { + flb_sds_destroy(ctx->region); + } + if (ctx->u) { + flb_upstream_destroy(ctx->u); + } + + metadata_fields_destroy(ctx); + + flb_free(ctx); + return 0; +} \ No newline at end of file diff --git a/plugins/out_oracle_log_analytics/oci_logan_conf.h b/plugins/out_oracle_log_analytics/oci_logan_conf.h new file mode 100644 index 00000000000..a11832b0a82 --- /dev/null +++ b/plugins/out_oracle_log_analytics/oci_logan_conf.h @@ -0,0 +1,34 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2023 The Fluent Bit Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef FLB_OUT_OCI_LOGAN_CONF_H +#define FLB_OUT_OCI_LOGAN_CONF_H + +#include +#include +#include + +#include "oci_logan.h" + +struct flb_oci_logan *flb_oci_logan_conf_create(struct flb_output_instance *ins, + struct flb_config *config); +int flb_oci_logan_conf_destroy(struct flb_oci_logan *ctx); + +#endif