diff --git a/plugins/filter_kubernetes/kube_conf.h b/plugins/filter_kubernetes/kube_conf.h index 85f90928c83..80ee6346a56 100644 --- a/plugins/filter_kubernetes/kube_conf.h +++ b/plugins/filter_kubernetes/kube_conf.h @@ -109,6 +109,7 @@ struct flb_kube { int owner_references; int namespace_labels; int namespace_annotations; + int namespace_exclude; int namespace_metadata_only; int dummy_meta; int tls_debug; diff --git a/plugins/filter_kubernetes/kube_meta.c b/plugins/filter_kubernetes/kube_meta.c index f8371b1a826..614cf9cfb10 100644 --- a/plugins/filter_kubernetes/kube_meta.c +++ b/plugins/filter_kubernetes/kube_meta.c @@ -1210,6 +1210,8 @@ static int merge_namespace_meta(struct flb_kube_meta *meta, struct flb_kube *ctx int have_labels = -1; int have_annotations = -1; size_t off = 0; + size_t prop_size; + void *prop_buf; msgpack_sbuffer mp_sbuf; msgpack_packer mp_pck; @@ -1219,6 +1221,8 @@ static int merge_namespace_meta(struct flb_kube_meta *meta, struct flb_kube *ctx msgpack_object v; msgpack_object meta_val; msgpack_object api_map; + msgpack_object ann_map; + struct flb_kube_props props = {0}; /* * @@ -1324,6 +1328,36 @@ static int merge_namespace_meta(struct flb_kube_meta *meta, struct flb_kube *ctx msgpack_pack_object(&mp_pck, v); } + /* Process namespace configuration suggested through annotations */ + if (have_annotations >= 0 && ctx->namespace_exclude == FLB_TRUE) { + ann_map = meta_val.via.map.ptr[have_annotations].val; + + if (ann_map.type == MSGPACK_OBJECT_MAP) { + for (i = 0; i < ann_map.via.map.size; i++) { + k = ann_map.via.map.ptr[i].key; + v = ann_map.via.map.ptr[i].val; + + if (k.type == MSGPACK_OBJECT_STR && + v.type == MSGPACK_OBJECT_STR && + k.via.str.size == sizeof("fluentbit.io/exclude") - 1 && + strncmp(k.via.str.ptr, "fluentbit.io/exclude", + sizeof("fluentbit.io/exclude") - 1) == 0) { + flb_kube_namespace_prop_set(ctx, meta, + k.via.str.ptr + 13, + k.via.str.size - 13, + v.via.str.ptr, + v.via.str.size, + &props); + } + } + } + + flb_kube_prop_pack(&props, &prop_buf, &prop_size); + msgpack_sbuffer_write(&mp_sbuf, prop_buf, prop_size); + flb_kube_prop_destroy(&props); + flb_free(prop_buf); + } + if (api_buf != NULL) { msgpack_unpacked_destroy(&api_result); if (meta_found == FLB_TRUE) { @@ -2531,7 +2565,8 @@ static inline int flb_kube_local_pod_meta_get(struct flb_kube *ctx, static inline int lookup_namespace_meta(struct flb_kube *ctx, const char **out_buf, size_t *out_size, - struct flb_kube_meta *meta) + struct flb_kube_meta *meta, + struct flb_kube_props *props) { int id; int ret; @@ -2539,6 +2574,7 @@ static inline int lookup_namespace_meta(struct flb_kube *ctx, char *tmp_hash_meta_buf; size_t off = 0; size_t hash_meta_size; + size_t namespace_meta_size; msgpack_unpacked result; /* Check if we have some data associated to the cache key */ @@ -2586,6 +2622,7 @@ static inline int lookup_namespace_meta(struct flb_kube *ctx, * The retrieved buffer may have serialized items: * * [0] = kubernetes metadata (annotations, labels) + * [1] = namespace annotation properties * */ msgpack_unpacked_init(&result); @@ -2599,9 +2636,24 @@ static inline int lookup_namespace_meta(struct flb_kube *ctx, return 0; } - /* Set the pointer and proper size for the caller */ + namespace_meta_size = off; + + /* Expose namespace metadata only when record injection is enabled */ *out_buf = hash_meta_buf; - *out_size = off; + if (ctx->namespace_labels == FLB_TRUE || + ctx->namespace_annotations == FLB_TRUE) { + *out_size = namespace_meta_size; + } + else { + *out_size = 0; + } + + ret = msgpack_unpack_next(&result, hash_meta_buf, hash_meta_size, &off); + if (ret == MSGPACK_UNPACK_SUCCESS) { + flb_kube_prop_unpack(props, + hash_meta_buf + namespace_meta_size, + hash_meta_size - namespace_meta_size); + } msgpack_unpacked_destroy(&result); @@ -2612,7 +2664,8 @@ static inline int flb_kube_namespace_meta_get(struct flb_kube *ctx, const char *tag, int tag_len, const char *data, size_t data_size, const char **out_buf, size_t *out_size, - struct flb_kube_meta *meta) + struct flb_kube_meta *meta, + struct flb_kube_props *props) { int ret; @@ -2622,12 +2675,13 @@ static inline int flb_kube_namespace_meta_get(struct flb_kube *ctx, return -1; } - return lookup_namespace_meta(ctx, out_buf, out_size, meta); + return lookup_namespace_meta(ctx, out_buf, out_size, meta, props); } static inline int flb_kube_local_namespace_meta_get(struct flb_kube *ctx, const char **out_buf, size_t *out_size, - struct flb_kube_meta *meta) + struct flb_kube_meta *meta, + struct flb_kube_props *props) { int ret; @@ -2636,7 +2690,7 @@ static inline int flb_kube_local_namespace_meta_get(struct flb_kube *ctx, return -1; } - return lookup_namespace_meta(ctx, out_buf, out_size, meta); + return lookup_namespace_meta(ctx, out_buf, out_size, meta, props); } int flb_kube_meta_get(struct flb_kube *ctx, @@ -2647,15 +2701,19 @@ int flb_kube_meta_get(struct flb_kube *ctx, size_t *namespace_out_size, struct flb_kube_meta *meta, struct flb_kube_props *props, - struct flb_kube_meta *namespace_meta + struct flb_kube_meta *namespace_meta, + struct flb_kube_props *namespace_props ) { int ret_namespace_meta = -1; int ret_pod_meta = -1; - if(ctx->namespace_labels == FLB_TRUE || ctx->namespace_annotations == FLB_TRUE) { + if (ctx->namespace_labels == FLB_TRUE || + ctx->namespace_annotations == FLB_TRUE || + ctx->namespace_exclude == FLB_TRUE) { ret_namespace_meta = flb_kube_namespace_meta_get(ctx, tag, tag_len, data, - data_size, namespace_out_buf, namespace_out_size, namespace_meta); + data_size, namespace_out_buf, namespace_out_size, + namespace_meta, namespace_props); } if(ctx->namespace_metadata_only == FLB_FALSE) { @@ -2677,15 +2735,19 @@ int flb_kube_meta_get_local(struct flb_kube *ctx, size_t *namespace_out_size, struct flb_kube_meta *meta, struct flb_kube_props *props, - struct flb_kube_meta *namespace_meta) + struct flb_kube_meta *namespace_meta, + struct flb_kube_props *namespace_props) { int ret_namespace_meta = -1; int ret_pod_meta = -1; - if (ctx->namespace_labels == FLB_TRUE || ctx->namespace_annotations == FLB_TRUE) { + if (ctx->namespace_labels == FLB_TRUE || + ctx->namespace_annotations == FLB_TRUE || + ctx->namespace_exclude == FLB_TRUE) { ret_namespace_meta = flb_kube_local_namespace_meta_get(ctx, namespace_out_buf, namespace_out_size, - namespace_meta); + namespace_meta, + namespace_props); } if (ctx->namespace_metadata_only == FLB_FALSE) { diff --git a/plugins/filter_kubernetes/kube_meta.h b/plugins/filter_kubernetes/kube_meta.h index 8a19e090692..21687e38fd7 100644 --- a/plugins/filter_kubernetes/kube_meta.h +++ b/plugins/filter_kubernetes/kube_meta.h @@ -76,14 +76,16 @@ int flb_kube_meta_get(struct flb_kube *ctx, size_t *namespace_out_size, struct flb_kube_meta *meta, struct flb_kube_props *props, - struct flb_kube_meta *namespace_meta); + struct flb_kube_meta *namespace_meta, + struct flb_kube_props *namespace_props); int flb_kube_meta_get_local(struct flb_kube *ctx, const char **out_buf, size_t *out_size, const char **namespace_out_buf, size_t *namespace_out_size, struct flb_kube_meta *meta, struct flb_kube_props *props, - struct flb_kube_meta *namespace_meta); + struct flb_kube_meta *namespace_meta, + struct flb_kube_props *namespace_props); int flb_kube_meta_release(struct flb_kube_meta *meta); int flb_kube_pod_association_init(struct flb_kube *ctx, struct flb_config *config); int get_api_server_configmap(struct flb_kube *ctx, const char *namespace, const char *configmap, char **out_buf, size_t *out_size); diff --git a/plugins/filter_kubernetes/kube_property.c b/plugins/filter_kubernetes/kube_property.c index d93ee131fcc..0e5dee6a081 100644 --- a/plugins/filter_kubernetes/kube_property.c +++ b/plugins/filter_kubernetes/kube_property.c @@ -49,13 +49,19 @@ static inline void prop_not_allowed(const char *prop, struct flb_kube_meta *meta /* Property: parser */ static int prop_set_parser(struct flb_kube *ctx, struct flb_kube_meta *meta, - int is_container_specific, int stream, + int is_namespace, int is_container_specific, int stream, const char *val_buf, size_t val_len, struct flb_kube_props *props) { char *tmp; struct flb_parser *parser; + if (is_namespace == FLB_TRUE) { + flb_plg_warn(ctx->ins, "annotation 'fluentbit.io/parser' is not supported " + "on namespaces (ns='%s')", meta->namespace); + return -1; + } + /* Parser property must be allowed by k8s-logging.parser */ if (ctx->k8s_logging_parser == FLB_FALSE) { prop_not_allowed("fluentbit.io/parser", meta, ctx); @@ -105,19 +111,14 @@ static int prop_set_parser(struct flb_kube *ctx, struct flb_kube_meta *meta, } static int prop_set_exclude(struct flb_kube *ctx, struct flb_kube_meta *meta, - int is_container_specific, int stream, + int is_namespace, int is_container_specific, int stream, const char *val_buf, size_t val_len, struct flb_kube_props *props) { char *tmp; + int bool_value; int exclude; - /* Exclude property must be allowed by k8s-logging.exclude */ - if (ctx->k8s_logging_exclude == FLB_FALSE) { - prop_not_allowed("fluentbit.io/exclude", meta, ctx); - return -1; - } - /* Get the bool value */ tmp = flb_strndup(val_buf, val_len); if (!tmp) { @@ -125,9 +126,32 @@ static int prop_set_exclude(struct flb_kube *ctx, struct flb_kube_meta *meta, return -1; } - exclude = flb_utils_bool(tmp) == FLB_TRUE ? + bool_value = flb_utils_bool(tmp); + if (bool_value != FLB_TRUE && bool_value != FLB_FALSE) { + flb_plg_warn(ctx->ins, "invalid boolean value '%s' for annotation " + "'fluentbit.io/exclude' (ns='%s' pod_name='%s')", + tmp, meta->namespace, meta->podname); + flb_free(tmp); + return -1; + } + + exclude = bool_value == FLB_TRUE ? FLB_KUBE_PROP_TRUE : FLB_KUBE_PROP_FALSE; + /* + * Namespace exclusion enables explicit Pod opt-ins. Pod exclusions still + * require the existing k8s-logging.exclude option. + */ + if ((is_namespace == FLB_TRUE && ctx->namespace_exclude == FLB_FALSE) || + (is_namespace == FLB_FALSE && + ctx->k8s_logging_exclude == FLB_FALSE && + (ctx->namespace_exclude == FLB_FALSE || + exclude != FLB_KUBE_PROP_FALSE))) { + prop_not_allowed("fluentbit.io/exclude", meta, ctx); + flb_free(tmp); + return -1; + } + /* Save the exclude property in the context */ if ((stream == FLB_KUBE_PROP_NO_STREAM || stream == FLB_KUBE_PROP_STREAM_STDOUT) && @@ -147,10 +171,10 @@ static int prop_set_exclude(struct flb_kube *ctx, struct flb_kube_meta *meta, return 0; } -int flb_kube_prop_set(struct flb_kube *ctx, struct flb_kube_meta *meta, - const char *prop, int prop_len, - const char *val_buf, size_t val_len, - struct flb_kube_props *props) +static int prop_set(struct flb_kube *ctx, struct flb_kube_meta *meta, + int is_namespace, const char *prop, int prop_len, + const char *val_buf, size_t val_len, + struct flb_kube_props *props) { /* * Property can take the following forms: @@ -167,7 +191,7 @@ int flb_kube_prop_set(struct flb_kube *ctx, struct flb_kube_meta *meta, size_t container_len = 0; int stream = FLB_KUBE_PROP_NO_STREAM; int (*function)(struct flb_kube *ctx, struct flb_kube_meta *meta, - int is_container_specific, int stream, + int is_namespace, int is_container_specific, int stream, const char *val_buf, size_t val_len, struct flb_kube_props *props); @@ -244,11 +268,38 @@ int flb_kube_prop_set(struct flb_kube *ctx, struct flb_kube_meta *meta, } } - return function(ctx, meta, + return function(ctx, meta, is_namespace, (container ? FLB_TRUE : FLB_FALSE), stream, val_buf, val_len, props); } +int flb_kube_prop_set(struct flb_kube *ctx, struct flb_kube_meta *meta, + const char *prop, int prop_len, + const char *val_buf, size_t val_len, + struct flb_kube_props *props) +{ + return prop_set(ctx, meta, FLB_FALSE, prop, prop_len, + val_buf, val_len, props); +} + +int flb_kube_namespace_prop_set(struct flb_kube *ctx, + struct flb_kube_meta *meta, + const char *prop, int prop_len, + const char *val_buf, size_t val_len, + struct flb_kube_props *props) +{ + if (prop_len != FLB_KUBE_PROP_EXCLUDE_LEN || + strncmp(prop, FLB_KUBE_PROP_EXCLUDE, FLB_KUBE_PROP_EXCLUDE_LEN) != 0) { + flb_plg_warn(ctx->ins, "namespace annotation 'fluentbit.io/%.*s' is " + "not supported; only 'fluentbit.io/exclude' is supported " + "(ns='%s')", prop_len, prop, meta->namespace); + return -1; + } + + return prop_set_exclude(ctx, meta, FLB_TRUE, FLB_FALSE, + FLB_KUBE_PROP_NO_STREAM, val_buf, val_len, props); +} + int flb_kube_prop_pack(struct flb_kube_props *props, void **out_buf, size_t *out_size) { @@ -288,17 +339,23 @@ int flb_kube_prop_pack(struct flb_kube_props *props, if (props->stdout_exclude == FLB_KUBE_PROP_TRUE) { msgpack_pack_true(&pck); } - else { + else if (props->stdout_exclude == FLB_KUBE_PROP_FALSE) { msgpack_pack_false(&pck); } + else { + msgpack_pack_nil(&pck); + } /* Index 3: FLB_KUBE_PROPS_STDERR_EXCLUDE */ if (props->stderr_exclude == FLB_KUBE_PROP_TRUE) { msgpack_pack_true(&pck); } - else { + else if (props->stderr_exclude == FLB_KUBE_PROP_FALSE) { msgpack_pack_false(&pck); } + else { + msgpack_pack_nil(&pck); + } /* Set outgoing msgpack buffer */ *out_buf = sbuf.data; @@ -346,11 +403,23 @@ int flb_kube_prop_unpack(struct flb_kube_props *props, /* Index 2: stdout_exclude */ o = root.via.array.ptr[FLB_KUBE_PROPS_STDOUT_EXCLUDE]; - props->stdout_exclude = o.via.boolean; + if (o.type == MSGPACK_OBJECT_BOOLEAN) { + props->stdout_exclude = o.via.boolean ? + FLB_KUBE_PROP_TRUE : FLB_KUBE_PROP_FALSE; + } + else { + props->stdout_exclude = FLB_KUBE_PROP_UNDEF; + } /* Index 3: stderr_exclude */ o = root.via.array.ptr[FLB_KUBE_PROPS_STDERR_EXCLUDE]; - props->stderr_exclude = o.via.boolean; + if (o.type == MSGPACK_OBJECT_BOOLEAN) { + props->stderr_exclude = o.via.boolean ? + FLB_KUBE_PROP_TRUE : FLB_KUBE_PROP_FALSE; + } + else { + props->stderr_exclude = FLB_KUBE_PROP_UNDEF; + } msgpack_unpacked_destroy(&result); return 0; @@ -367,4 +436,7 @@ void flb_kube_prop_destroy(struct flb_kube_props *props) flb_sds_destroy(props->stderr_parser); props->stderr_parser = NULL; } + + props->stdout_exclude = FLB_KUBE_PROP_UNDEF; + props->stderr_exclude = FLB_KUBE_PROP_UNDEF; } diff --git a/plugins/filter_kubernetes/kube_property.h b/plugins/filter_kubernetes/kube_property.h index a144c45fefa..328d84358bf 100644 --- a/plugins/filter_kubernetes/kube_property.h +++ b/plugins/filter_kubernetes/kube_property.h @@ -32,6 +32,11 @@ int flb_kube_prop_set(struct flb_kube *ctx, struct flb_kube_meta *meta, const char *prop, int prop_len, const char *val_buf, size_t val_len, struct flb_kube_props *props); +int flb_kube_namespace_prop_set(struct flb_kube *ctx, + struct flb_kube_meta *meta, + const char *prop, int prop_len, + const char *val_buf, size_t val_len, + struct flb_kube_props *props); int flb_kube_prop_pack(struct flb_kube_props *props, void **out_buf, size_t *out_size); int flb_kube_prop_unpack(struct flb_kube_props *props, const char *buf, size_t size); diff --git a/plugins/filter_kubernetes/kube_props.h b/plugins/filter_kubernetes/kube_props.h index 5ee027f5934..26a3755c27a 100644 --- a/plugins/filter_kubernetes/kube_props.h +++ b/plugins/filter_kubernetes/kube_props.h @@ -37,8 +37,8 @@ struct flb_kube_props { flb_sds_t stdout_parser; /* suggested parser for stdout */ flb_sds_t stderr_parser; /* suggested parser for stderr */ - int stdout_exclude; /* bool: exclude stdout logs ? */ - int stderr_exclude; /* bool: exclude stderr logs ? */ + int stdout_exclude; /* tri-state: inherit, include, or exclude stdout */ + int stderr_exclude; /* tri-state: inherit, include, or exclude stderr */ }; #endif diff --git a/plugins/filter_kubernetes/kubernetes.c b/plugins/filter_kubernetes/kubernetes.c index e272b3d321b..6737021170d 100644 --- a/plugins/filter_kubernetes/kubernetes.c +++ b/plugins/filter_kubernetes/kubernetes.c @@ -136,6 +136,15 @@ static int get_stream(msgpack_object_map map) return FLB_KUBE_PROP_NO_STREAM; } +static int should_exclude(int pod_property, int namespace_property) +{ + if (pod_property != FLB_KUBE_PROP_UNDEF) { + return pod_property == FLB_KUBE_PROP_TRUE; + } + + return namespace_property == FLB_KUBE_PROP_TRUE; +} + static int value_trim_size(msgpack_object o) { int i; @@ -665,6 +674,7 @@ static int cb_kube_filter(const void *data, size_t bytes, struct flb_kube_meta meta = {0}; struct flb_kube_props props = {0}; struct flb_kube_meta namespace_meta = {0}; + struct flb_kube_props namespace_props = {0}; struct flb_log_event_encoder log_encoder; struct flb_log_event_decoder log_decoder; struct flb_log_event log_event; @@ -685,7 +695,7 @@ static int cb_kube_filter(const void *data, size_t bytes, &namespace_cache_buf, &namespace_cache_size, &meta, &props, - &namespace_meta); + &namespace_meta, &namespace_props); } else { /* Check if we have some cached metadata for the incoming events */ @@ -695,7 +705,7 @@ static int cb_kube_filter(const void *data, size_t bytes, &cache_buf, &cache_size, &namespace_cache_buf, &namespace_cache_size, &meta, &props, - &namespace_meta); + &namespace_meta, &namespace_props); } if (ret == -1) { return FLB_FILTER_NOTOUCH; @@ -711,6 +721,7 @@ static int cb_kube_filter(const void *data, size_t bytes, flb_kube_meta_release(&meta); flb_kube_prop_destroy(&props); flb_kube_meta_release(&namespace_meta); + flb_kube_prop_destroy(&namespace_props); return FLB_FILTER_NOTOUCH; } @@ -726,6 +737,7 @@ static int cb_kube_filter(const void *data, size_t bytes, flb_kube_meta_release(&meta); flb_kube_prop_destroy(&props); flb_kube_meta_release(&namespace_meta); + flb_kube_prop_destroy(&namespace_props); return FLB_FILTER_NOTOUCH; } @@ -748,7 +760,7 @@ static int cb_kube_filter(const void *data, size_t bytes, &cache_buf, &cache_size, &namespace_cache_buf, &namespace_cache_size, &meta, &props, - &namespace_meta); + &namespace_meta, &namespace_props); if (ret == -1) { continue; } @@ -761,12 +773,14 @@ static int cb_kube_filter(const void *data, size_t bytes, switch (get_stream(log_event.body->via.map)) { case FLB_KUBE_PROP_STREAM_STDOUT: { - if (props.stdout_exclude == FLB_TRUE) { + if (should_exclude(props.stdout_exclude, + namespace_props.stdout_exclude)) { /* Skip this record */ if (ctx->use_journal == FLB_TRUE) { flb_kube_meta_release(&meta); flb_kube_prop_destroy(&props); flb_kube_meta_release(&namespace_meta); + flb_kube_prop_destroy(&namespace_props); } continue; } @@ -777,12 +791,14 @@ static int cb_kube_filter(const void *data, size_t bytes, break; case FLB_KUBE_PROP_STREAM_STDERR: { - if (props.stderr_exclude == FLB_TRUE) { + if (should_exclude(props.stderr_exclude, + namespace_props.stderr_exclude)) { /* Skip this record */ if (ctx->use_journal == FLB_TRUE) { flb_kube_meta_release(&meta); flb_kube_prop_destroy(&props); flb_kube_meta_release(&namespace_meta); + flb_kube_prop_destroy(&namespace_props); } continue; } @@ -793,8 +809,16 @@ static int cb_kube_filter(const void *data, size_t bytes, break; default: { - if (props.stdout_exclude == props.stderr_exclude && - props.stderr_exclude == FLB_TRUE) { + if (should_exclude(props.stdout_exclude, + namespace_props.stdout_exclude) && + should_exclude(props.stderr_exclude, + namespace_props.stderr_exclude)) { + if (ctx->use_journal == FLB_TRUE) { + flb_kube_meta_release(&meta); + flb_kube_prop_destroy(&props); + flb_kube_meta_release(&namespace_meta); + flb_kube_prop_destroy(&namespace_props); + } continue; } if (props.stdout_parser == props.stderr_parser && @@ -811,7 +835,7 @@ static int cb_kube_filter(const void *data, size_t bytes, ret = flb_log_event_encoder_begin_record(&log_encoder); if (ret != FLB_EVENT_ENCODER_SUCCESS) { - break; + goto record_cleanup; } ret = pack_map_content(&log_encoder, @@ -830,6 +854,7 @@ static int cb_kube_filter(const void *data, size_t bytes, flb_kube_meta_release(&meta); flb_kube_prop_destroy(&props); flb_kube_meta_release(&namespace_meta); + flb_kube_prop_destroy(&namespace_props); return FLB_FILTER_NOTOUCH; } @@ -838,14 +863,18 @@ static int cb_kube_filter(const void *data, size_t bytes, if (ret != FLB_EVENT_ENCODER_SUCCESS) { flb_log_event_encoder_rollback_record(&log_encoder); - - break; } +record_cleanup: if (ctx->use_journal == FLB_TRUE) { flb_kube_meta_release(&meta); flb_kube_prop_destroy(&props); flb_kube_meta_release(&namespace_meta); + flb_kube_prop_destroy(&namespace_props); + } + + if (ret != FLB_EVENT_ENCODER_SUCCESS) { + break; } } @@ -854,6 +883,7 @@ static int cb_kube_filter(const void *data, size_t bytes, flb_kube_meta_release(&meta); flb_kube_prop_destroy(&props); flb_kube_meta_release(&namespace_meta); + flb_kube_prop_destroy(&namespace_props); } if (ctx->dummy_meta == FLB_TRUE) { @@ -1070,6 +1100,13 @@ static struct flb_config_map config_map[] = { 0, FLB_TRUE, offsetof(struct flb_kube, namespace_annotations), "include Kubernetes namespace annotations on every record" }, + /* Allow Kubernetes Namespaces to exclude their Pods' logs ? */ + { + FLB_CONFIG_MAP_BOOL, "namespace_exclude", "false", + 0, FLB_TRUE, offsetof(struct flb_kube, namespace_exclude), + "allow namespaces to exclude their pods' logs via the fluentbit.io/exclude " + "annotation (requires access to the namespaces API)" + }, /* Ignore pod metadata entirely, useful for fetching only namespace meta */ { FLB_CONFIG_MAP_BOOL, "namespace_metadata_only", "false", diff --git a/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-false_base_text.log b/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-false_base_text.log new file mode 100644 index 00000000000..59ad227ff2d --- /dev/null +++ b/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-false_base_text.log @@ -0,0 +1 @@ +{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"} diff --git a/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-stdout_base_text.log b/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-stdout_base_text.log new file mode 100644 index 00000000000..d5cd3af5501 --- /dev/null +++ b/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-stdout_base_text.log @@ -0,0 +1,2 @@ +{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"} +{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"} diff --git a/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-true_base_text.log b/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-true_base_text.log new file mode 100644 index 00000000000..59ad227ff2d --- /dev/null +++ b/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-true_base_text.log @@ -0,0 +1 @@ +{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"} diff --git a/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-true_invalid-override_text.log b/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-true_invalid-override_text.log new file mode 100644 index 00000000000..59ad227ff2d --- /dev/null +++ b/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-true_invalid-override_text.log @@ -0,0 +1 @@ +{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"} diff --git a/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-true_override_text.log b/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-true_override_text.log new file mode 100644 index 00000000000..59ad227ff2d --- /dev/null +++ b/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-true_override_text.log @@ -0,0 +1 @@ +{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"} diff --git a/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-true_stream-override_text.log b/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-true_stream-override_text.log new file mode 100644 index 00000000000..d5cd3af5501 --- /dev/null +++ b/tests/runtime/data/kubernetes/log/namespace-exclude/namespace-exclude-true_stream-override_text.log @@ -0,0 +1,2 @@ +{"log":"Simple text\n","stream":"stdout","time":"2019-04-01T17:58:33.598656444Z"} +{"log":"Simple text\n","stream":"stderr","time":"2019-04-01T17:58:33.698656444Z"} diff --git a/tests/runtime/data/kubernetes/meta/annotations-exclude.namespace_meta b/tests/runtime/data/kubernetes/meta/annotations-exclude.namespace_meta new file mode 100644 index 00000000000..e7757653931 --- /dev/null +++ b/tests/runtime/data/kubernetes/meta/annotations-exclude.namespace_meta @@ -0,0 +1,6 @@ +{ + "metadata": { + "annotations": {}, + "name": "annotations-exclude" + } +} diff --git a/tests/runtime/data/kubernetes/meta/namespace-exclude-false.namespace_meta b/tests/runtime/data/kubernetes/meta/namespace-exclude-false.namespace_meta new file mode 100644 index 00000000000..0edc50ae0db --- /dev/null +++ b/tests/runtime/data/kubernetes/meta/namespace-exclude-false.namespace_meta @@ -0,0 +1,8 @@ +{ + "metadata": { + "annotations": { + "fluentbit.io/exclude": "false" + }, + "name": "namespace-exclude-false" + } +} diff --git a/tests/runtime/data/kubernetes/meta/namespace-exclude-false_base.meta b/tests/runtime/data/kubernetes/meta/namespace-exclude-false_base.meta new file mode 100644 index 00000000000..5937a866595 --- /dev/null +++ b/tests/runtime/data/kubernetes/meta/namespace-exclude-false_base.meta @@ -0,0 +1,3 @@ +{ + "metadata": {} +} diff --git a/tests/runtime/data/kubernetes/meta/namespace-exclude-stdout.namespace_meta b/tests/runtime/data/kubernetes/meta/namespace-exclude-stdout.namespace_meta new file mode 100644 index 00000000000..4673555e3b1 --- /dev/null +++ b/tests/runtime/data/kubernetes/meta/namespace-exclude-stdout.namespace_meta @@ -0,0 +1,8 @@ +{ + "metadata": { + "annotations": { + "fluentbit.io/exclude_stdout": "true" + }, + "name": "namespace-exclude-stdout" + } +} diff --git a/tests/runtime/data/kubernetes/meta/namespace-exclude-stdout_base.meta b/tests/runtime/data/kubernetes/meta/namespace-exclude-stdout_base.meta new file mode 100644 index 00000000000..5937a866595 --- /dev/null +++ b/tests/runtime/data/kubernetes/meta/namespace-exclude-stdout_base.meta @@ -0,0 +1,3 @@ +{ + "metadata": {} +} diff --git a/tests/runtime/data/kubernetes/meta/namespace-exclude-true.namespace_meta b/tests/runtime/data/kubernetes/meta/namespace-exclude-true.namespace_meta new file mode 100644 index 00000000000..f62a4977246 --- /dev/null +++ b/tests/runtime/data/kubernetes/meta/namespace-exclude-true.namespace_meta @@ -0,0 +1,8 @@ +{ + "metadata": { + "annotations": { + "fluentbit.io/exclude": "true" + }, + "name": "namespace-exclude-true" + } +} diff --git a/tests/runtime/data/kubernetes/meta/namespace-exclude-true_base.meta b/tests/runtime/data/kubernetes/meta/namespace-exclude-true_base.meta new file mode 100644 index 00000000000..5937a866595 --- /dev/null +++ b/tests/runtime/data/kubernetes/meta/namespace-exclude-true_base.meta @@ -0,0 +1,3 @@ +{ + "metadata": {} +} diff --git a/tests/runtime/data/kubernetes/meta/namespace-exclude-true_invalid-override.meta b/tests/runtime/data/kubernetes/meta/namespace-exclude-true_invalid-override.meta new file mode 100644 index 00000000000..d55fa2ea952 --- /dev/null +++ b/tests/runtime/data/kubernetes/meta/namespace-exclude-true_invalid-override.meta @@ -0,0 +1,7 @@ +{ + "metadata": { + "annotations": { + "fluentbit.io/exclude": "flase" + } + } +} diff --git a/tests/runtime/data/kubernetes/meta/namespace-exclude-true_override.meta b/tests/runtime/data/kubernetes/meta/namespace-exclude-true_override.meta new file mode 100644 index 00000000000..065df3b1ef5 --- /dev/null +++ b/tests/runtime/data/kubernetes/meta/namespace-exclude-true_override.meta @@ -0,0 +1,7 @@ +{ + "metadata": { + "annotations": { + "fluentbit.io/exclude": "false" + } + } +} diff --git a/tests/runtime/data/kubernetes/meta/namespace-exclude-true_stream-override.meta b/tests/runtime/data/kubernetes/meta/namespace-exclude-true_stream-override.meta new file mode 100644 index 00000000000..4436eb8e8ff --- /dev/null +++ b/tests/runtime/data/kubernetes/meta/namespace-exclude-true_stream-override.meta @@ -0,0 +1,7 @@ +{ + "metadata": { + "annotations": { + "fluentbit.io/exclude_stdout": "false" + } + } +} diff --git a/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-false_base_text.out b/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-false_base_text.out new file mode 100644 index 00000000000..f1ccfa7e623 --- /dev/null +++ b/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-false_base_text.out @@ -0,0 +1 @@ +[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"base","namespace_name":"namespace-exclude-false","container_name":"text"}}] diff --git a/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-stdout_base_text_stderr.out b/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-stdout_base_text_stderr.out new file mode 100644 index 00000000000..08cad4e3b9d --- /dev/null +++ b/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-stdout_base_text_stderr.out @@ -0,0 +1 @@ +[1554141513.698657,{"log":"Simple text\n","stream":"stderr","kubernetes":{"pod_name":"base","namespace_name":"namespace-exclude-stdout","container_name":"text"}}] diff --git a/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-stdout_base_text_stdout.out b/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-stdout_base_text_stdout.out new file mode 100644 index 00000000000..66c3c9bedbf --- /dev/null +++ b/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-stdout_base_text_stdout.out @@ -0,0 +1 @@ +[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"base","namespace_name":"namespace-exclude-stdout","container_name":"text"}}] diff --git a/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-true_base_text.out b/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-true_base_text.out new file mode 100644 index 00000000000..79dfd33ee76 --- /dev/null +++ b/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-true_base_text.out @@ -0,0 +1 @@ +[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"base","namespace_name":"namespace-exclude-true","container_name":"text"}}] diff --git a/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-true_override_text.out b/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-true_override_text.out new file mode 100644 index 00000000000..4fd39b8fe4c --- /dev/null +++ b/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-true_override_text.out @@ -0,0 +1 @@ +[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"override","namespace_name":"namespace-exclude-true","annotations":{"fluentbit.io/exclude":"false"},"container_name":"text"}}] diff --git a/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-true_stream-override_text_stdout.out b/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-true_stream-override_text_stdout.out new file mode 100644 index 00000000000..6e5a1d9c043 --- /dev/null +++ b/tests/runtime/data/kubernetes/out/namespace-exclude/namespace-exclude-true_stream-override_text_stdout.out @@ -0,0 +1 @@ +[1554141513.598656,{"log":"Simple text\n","stream":"stdout","kubernetes":{"pod_name":"stream-override","namespace_name":"namespace-exclude-true","annotations":{"fluentbit.io/exclude_stdout":"false"},"container_name":"text"}}] diff --git a/tests/runtime/filter_kubernetes.c b/tests/runtime/filter_kubernetes.c index 751041a0851..8138234ae46 100644 --- a/tests/runtime/filter_kubernetes.c +++ b/tests/runtime/filter_kubernetes.c @@ -236,6 +236,7 @@ static void kube_test(const char *target, int type, const char *suffix, int nExp int in_ffd; int filter_ffd; int out_ffd; + int wait_for_zero = FLB_FALSE; char *key; char *value; char path[PATH_MAX]; @@ -309,6 +310,10 @@ static void kube_test(const char *target, int type, const char *suffix, int nExp /* Wrong parameter */ break; } + if (strcasecmp(key, "Namespace_Exclude") == 0 && + strcasecmp(value, "On") == 0) { + wait_for_zero = FLB_TRUE; + } ret = flb_filter_set(ctx.flb, filter_ffd, key, value, NULL); TEST_CHECK_(ret == 0, "setting filter additional options"); } @@ -371,8 +376,13 @@ static void kube_test(const char *target, int type, const char *suffix, int nExp } #endif - /* Wait until matching nExpected results */ - wait_with_timeout(KUBE_TEST_TIMEOUT_MS, &result.nMatched, nExpected); + /* Zero-output namespace exclusion cases must observe the full timeout. */ + if (nExpected == 0 && wait_for_zero == FLB_TRUE) { + flb_time_msleep(KUBE_TEST_TIMEOUT_MS); + } + else { + wait_with_timeout(KUBE_TEST_TIMEOUT_MS, &result.nMatched, nExpected); + } TEST_CHECK(result.nMatched == nExpected); TEST_MSG("result.nMatched: %i\nnExpected: %i", result.nMatched, nExpected); @@ -603,6 +613,85 @@ static void flb_test_core_base_with_namespace_labels_and_annotations() flb_test_namespace_labels_and_annotations("core_base-with-namespace-labels-and-annotations_fluent-bit", NULL, 1); } +#define flb_test_namespace_exclude(target, suffix, nExpected, ...) \ + kube_test("namespace-exclude/" target, KUBE_TAIL, suffix, nExpected, \ + __VA_ARGS__, \ + NULL); \ + +static void flb_test_namespace_exclude_enabled() +{ + flb_test_namespace_exclude("namespace-exclude-true_base_text", NULL, 0, + "Namespace_Exclude", "On"); +} + +static void flb_test_namespace_exclude_false() +{ + flb_test_namespace_exclude("namespace-exclude-false_base_text", NULL, 1, + "Namespace_Exclude", "On"); +} + +static void flb_test_namespace_exclude_disabled() +{ + flb_test_namespace_exclude("namespace-exclude-true_base_text", NULL, 1, + "Namespace_Exclude", "Off"); +} + +static void flb_test_namespace_exclude_ignores_stdout_annotation_stdout() +{ + flb_test_namespace_exclude("namespace-exclude-stdout_base_text", "stdout", 1, + "Namespace_Exclude", "On"); +} + +static void flb_test_namespace_exclude_ignores_stdout_annotation_stderr() +{ + flb_test_namespace_exclude("namespace-exclude-stdout_base_text", "stderr", 1, + "Namespace_Exclude", "On"); +} + +static void flb_test_namespace_exclude_metadata_only() +{ + flb_test_namespace_exclude("namespace-exclude-true_base_text", NULL, 0, + "Namespace_Exclude", "On", + "Namespace_Metadata_Only", "On"); +} + +static void flb_test_namespace_exclude_with_pod_exclude() +{ + kube_test("annotations-exclude/annotations-exclude_default_text", + KUBE_TAIL, + NULL, + 0, + "Namespace_Exclude", "On", + "K8s-Logging.Exclude", "On", + NULL); +} + +static void flb_test_namespace_exclude_pod_override() +{ + flb_test_namespace_exclude("namespace-exclude-true_override_text", NULL, 1, + "Namespace_Exclude", "On"); +} + +static void flb_test_namespace_exclude_invalid_pod_override() +{ + flb_test_namespace_exclude("namespace-exclude-true_invalid-override_text", NULL, 0, + "Namespace_Exclude", "On"); +} + +static void flb_test_namespace_exclude_pod_stdout_override_stdout() +{ + flb_test_namespace_exclude("namespace-exclude-true_stream-override_text", + "stdout", 1, + "Namespace_Exclude", "On"); +} + +static void flb_test_namespace_exclude_pod_stdout_override_stderr() +{ + flb_test_namespace_exclude("namespace-exclude-true_stream-override_text", + "stderr", 0, + "Namespace_Exclude", "On"); +} + static void flb_test_kube_short_prefix_uat_podname() { kube_test("core/core_uat-myapp-12345_fluent-bit", @@ -1226,6 +1315,22 @@ TEST_LIST = { {"kube_core_unescaping_text", flb_test_core_unescaping_text}, {"kube_core_unescaping_json", flb_test_core_unescaping_json}, {"kube_core_base_with_namespace_labels_and_annotations", flb_test_core_base_with_namespace_labels_and_annotations}, + {"kube_namespace_exclude_enabled", flb_test_namespace_exclude_enabled}, + {"kube_namespace_exclude_false", flb_test_namespace_exclude_false}, + {"kube_namespace_exclude_disabled", flb_test_namespace_exclude_disabled}, + {"kube_namespace_exclude_ignores_stdout_annotation_stdout", + flb_test_namespace_exclude_ignores_stdout_annotation_stdout}, + {"kube_namespace_exclude_ignores_stdout_annotation_stderr", + flb_test_namespace_exclude_ignores_stdout_annotation_stderr}, + {"kube_namespace_exclude_metadata_only", flb_test_namespace_exclude_metadata_only}, + {"kube_namespace_exclude_with_pod_exclude", flb_test_namespace_exclude_with_pod_exclude}, + {"kube_namespace_exclude_pod_override", flb_test_namespace_exclude_pod_override}, + {"kube_namespace_exclude_invalid_pod_override", + flb_test_namespace_exclude_invalid_pod_override}, + {"kube_namespace_exclude_pod_stdout_override_stdout", + flb_test_namespace_exclude_pod_stdout_override_stdout}, + {"kube_namespace_exclude_pod_stdout_override_stderr", + flb_test_namespace_exclude_pod_stdout_override_stderr}, {"kube_core_base_with_owner_references", flb_test_core_base_with_owner_references}, {"kube_local_fluentbit_logs", flb_test_local_fluentbit_logs}, {"kube_pod_association_multiple_instances", flb_test_pod_association_multiple_instances},