Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion src/flb_env.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ const char *flb_env_get(struct flb_env *env, const char *key)
/*
* Given a 'value', lookup for variables, if found, return a new composed
* sds string.
*
* Supports bash-style default substitution inside ${...}:
* ${name:-word} — if unset or empty, expand word; do not assign.
*/
flb_sds_t flb_env_var_translate(struct flb_env *env, const char *value)
{
Expand All @@ -211,6 +214,9 @@ flb_sds_t flb_env_var_translate(struct flb_env *env, const char *value)
int pre_var;
int have_var = FLB_FALSE;
const char *env_var = NULL;
char *v_sep;
const char *def_val;
int def_len;
char *v_start = NULL;
char *v_end = NULL;
char tmp[4096];
Expand Down Expand Up @@ -248,6 +254,18 @@ flb_sds_t flb_env_var_translate(struct flb_env *env, const char *value)
strncpy(tmp, v_start, v_len);
tmp[v_len] = '\0';
have_var = FLB_TRUE;

/* Bash-style default value expansion :- */
v_sep = strstr(tmp, ":");
def_val = NULL;
def_len = 0;

if (v_sep && (v_sep[1] == '-')) {
def_val = v_sep + 2;
def_len = strlen(def_val);
*v_sep = '\0';
}


/* Append pre-variable content */
pre_var = (v_start - 2) - (value + i);
Expand All @@ -264,7 +282,8 @@ flb_sds_t flb_env_var_translate(struct flb_env *env, const char *value)

/* Lookup the variable in our env-hash */
env_var = flb_env_get(env, tmp);
if (env_var) {
/* Skip env if it's empty and have a fallback defined */
if (env_var && !(def_val && strlen(env_var) == 0)) {
e_len = strlen(env_var);
s = buf_append(buf, env_var, e_len);
if (!s) {
Expand All @@ -275,6 +294,16 @@ flb_sds_t flb_env_var_translate(struct flb_env *env, const char *value)
buf = s;
}
}
else if (def_val) {
if (def_len > 0) {
s = buf_append(buf, def_val, def_len);
if (!s) {
flb_sds_destroy(buf);
return NULL;
}
buf = s;
}
}
else if (env->warn_unused == FLB_TRUE) {
flb_warn("[env] variable ${%s} is used but not set", tmp);
}
Expand Down
156 changes: 156 additions & 0 deletions tests/internal/env.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,163 @@ void test_translate_long_env()
}


/* ${name:-word} when unset or empty */
void test_expand_default_hyphen()
{
struct flb_env *env;
flb_sds_t buf = NULL;
const char *v;
int ret;

env = flb_env_create();
if (!TEST_CHECK(env != NULL)) {
TEST_MSG("flb_env_create failed");
return;
}

buf = flb_env_var_translate(env, "${FLB_IT_DEFHYPH_Z:-fallback}");
if (!TEST_CHECK(buf != NULL)) {
TEST_MSG("translate failed");
flb_env_destroy(env);
return;
}
if (!TEST_CHECK(strcmp(buf, "fallback") == 0)) {
TEST_MSG("expected fallback, got=%s", buf);
}
flb_sds_destroy(buf);

v = flb_env_get(env, "FLB_IT_DEFHYPH_Z");
if (!TEST_CHECK(v == NULL)) {
TEST_MSG(":- must not assign locally");
}

ret = flb_env_set(env, "FLB_IT_DEFHYPH_Z", "");
if (!TEST_CHECK(ret >= 0)) {
TEST_MSG("flb_env_set empty failed");
flb_env_destroy(env);
return;
}
buf = flb_env_var_translate(env, "${FLB_IT_DEFHYPH_Z:-empty_fallback}");
if (!TEST_CHECK(buf != NULL)) {
TEST_MSG("translate empty failed");
flb_env_destroy(env);
return;
}
if (!TEST_CHECK(strcmp(buf, "empty_fallback") == 0)) {
TEST_MSG("empty local should use default, got=%s", buf);
}
flb_sds_destroy(buf);
flb_env_destroy(env);
}


/* ${name:-} should return an empty string if name is unset or empty */
void test_expand_empty_default()
{
struct flb_env *env;
flb_sds_t buf = NULL;
int ret;

env = flb_env_create();
if (!TEST_CHECK(env != NULL)) {
TEST_MSG("flb_env_create failed");
return;
}

/* Case 1: Variable is unset */
buf = flb_env_var_translate(env, "val=${VAR_NOT_SET:-}");
if (!TEST_CHECK(buf != NULL)) {
TEST_MSG("translate failed");
flb_env_destroy(env);
return;
}
if (!TEST_CHECK(strcmp(buf, "val=") == 0)) {
TEST_MSG("expected 'val=', got='%s'", buf);
}
flb_sds_destroy(buf);

/* Case 2: Variable is explicitly set to empty string */
ret = flb_env_set(env, "VAR_EMPTY", "");
if (!TEST_CHECK(ret >= 0)) {
TEST_MSG("flb_env_set failed");
flb_env_destroy(env);
return;
}

buf = flb_env_var_translate(env, "${VAR_EMPTY:-}");
if (!TEST_CHECK(buf != NULL)) {
TEST_MSG("translate failed");
flb_env_destroy(env);
return;
}

/* Result should be length 0 */
if (!TEST_CHECK(strlen(buf) == 0)) {
TEST_MSG("expected empty string, got='%s'", buf);
}

flb_sds_destroy(buf);
flb_env_destroy(env);
}


void test_expand_default_hyphen_from_os()
{
struct flb_env *env;
flb_sds_t buf = NULL;
char *var_name = "FLB_IT_DEFHYPH_OS";
char *var_val = "from_process";
int ret;

/* Set OS environment inline */
#ifdef FLB_SYSTEM_WINDOWS
ret = _putenv_s(var_name, var_val);
#else
ret = setenv(var_name, var_val, 1);
#endif

if (!TEST_CHECK(ret == 0)) {
TEST_MSG("putenv failed");
return;
}

env = flb_env_create();
if (!TEST_CHECK(env != NULL)) {
TEST_MSG("flb_env_create failed");
#ifdef FLB_SYSTEM_WINDOWS
_putenv_s(var_name, "");
#else
unsetenv(var_name);
#endif
return;
}

buf = flb_env_var_translate(env, "${FLB_IT_DEFHYPH_OS:-fallback}");
if (!TEST_CHECK(buf != NULL)) {
TEST_MSG("translate failed");
}
else {
if (!TEST_CHECK(strcmp(buf, var_val) == 0)) {
TEST_MSG("expected from_process, got=%s", buf);
}
flb_sds_destroy(buf);
}

flb_env_destroy(env);

/* Unset OS environment inline */
#ifdef FLB_SYSTEM_WINDOWS
_putenv_s(var_name, "");
#else
unsetenv(var_name);
#endif
}


TEST_LIST = {
{ "translate_long_env" , test_translate_long_env},
{ "expand_default_hyphen" , test_expand_default_hyphen},
{ "expand_empty_default", test_expand_empty_default},
{ "expand_default_hyphen_from_os", test_expand_default_hyphen_from_os},
{ NULL, NULL }
};
Loading