Skip to content

Commit

Permalink
DYNCFG support deleting orphan configurations (netdata#17023)
Browse files Browse the repository at this point in the history
* dyncfg: allow deleting orphan configs

* restore applied text

* make the dyncfg applied text more descriptive
  • Loading branch information
ktsaou authored Feb 16, 2024
1 parent 23e9e99 commit 81cb35d
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 28 deletions.
65 changes: 40 additions & 25 deletions src/daemon/config/dyncfg-intercept.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,38 +225,44 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
char *cmd_str = get_word(words, num_words, i++);

if(!config || !*config || strcmp(config, PLUGINSD_FUNCTION_CONFIG) != 0)
return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: this is not a dyncfg request");
return dyncfg_intercept_early_error(
rfe, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: this is not a dyncfg request");

cmd = dyncfg_cmds2id(cmd_str);
if(cmd == DYNCFG_CMD_NONE)
return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: invalid command received");
return dyncfg_intercept_early_error(
rfe, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: invalid command received");

if(cmd == DYNCFG_CMD_ADD) {
add_name = get_word(words, num_words, i++);

if(!add_name || !*add_name)
return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: this action requires a name");
return dyncfg_intercept_early_error(
rfe, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: this action requires a name");

if(!called_from_dyncfg_echo) {
char nid[strlen(id) + strlen(add_name) + 2];
snprintfz(nid, sizeof(nid), "%s:%s", id, add_name);

if (dictionary_get(dyncfg_globals.nodes, nid))
return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: a configuration with this name already exists");
return dyncfg_intercept_early_error(
rfe, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: a configuration with this name already exists");
}
}

if((cmd == DYNCFG_CMD_ADD || cmd == DYNCFG_CMD_UPDATE || cmd == DYNCFG_CMD_TEST) && !has_payload)
return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: this action requires a payload");
return dyncfg_intercept_early_error(
rfe, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: this action requires a payload");

if((cmd != DYNCFG_CMD_ADD && cmd != DYNCFG_CMD_UPDATE && cmd != DYNCFG_CMD_TEST) && has_payload)
return dyncfg_intercept_early_error(rfe, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: this action does not require a payload");
return dyncfg_intercept_early_error(
rfe, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: this action does not require a payload");

item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, id);
if(!item) {
Expand All @@ -266,7 +272,9 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
}

if(!item)
return dyncfg_intercept_early_error(rfe, HTTP_RESP_NOT_FOUND, "dyncfg functions intercept: id is not found");
return dyncfg_intercept_early_error(
rfe, HTTP_RESP_NOT_FOUND,
"dyncfg functions intercept: id is not found");
}

DYNCFG *df = dictionary_acquired_item_value(item);
Expand All @@ -278,8 +286,9 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
case DYNCFG_CMD_SCHEMA:
if(!http_access_user_has_enough_access_level_for_endpoint(rfe->user_access, df->view_access)) {
make_the_call_to_plugin = false;
rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_FORBIDDEN,
"dyncfg: you don't have enough view permissions to execute this command");
rc = dyncfg_default_response(
rfe->result.wb, HTTP_RESP_FORBIDDEN,
"dyncfg: you don't have enough view permissions to execute this command");
}
break;

Expand All @@ -292,15 +301,17 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
case DYNCFG_CMD_RESTART:
if(!http_access_user_has_enough_access_level_for_endpoint(rfe->user_access, df->edit_access)) {
make_the_call_to_plugin = false;
rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_FORBIDDEN,
"dyncfg: you don't have enough edit permissions to execute this command");
rc = dyncfg_default_response(
rfe->result.wb, HTTP_RESP_FORBIDDEN,
"dyncfg: you don't have enough edit permissions to execute this command");
}
break;

default: {
make_the_call_to_plugin = false;
rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_INTERNAL_SERVER_ERROR,
"dyncfg: permissions for this command are not set");
rc = dyncfg_default_response(
rfe->result.wb, HTTP_RESP_INTERNAL_SERVER_ERROR,
"dyncfg: permissions for this command are not set");
}
break;
}
Expand All @@ -320,8 +331,9 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
else if (cmd == DYNCFG_CMD_ADD) {
if (df->type != DYNCFG_TYPE_TEMPLATE) {
make_the_call_to_plugin = false;
rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: add command is only allowed in templates");
rc = dyncfg_default_response(
rfe->result.wb, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: add command is only allowed in templates");

nd_log(NDLS_DAEMON, NDLP_ERR,
"DYNCFG: add command can only be applied on templates, not %s: %s",
Expand All @@ -331,11 +343,14 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
else if (
cmd == DYNCFG_CMD_ENABLE && df->type == DYNCFG_TYPE_JOB &&
dyncfg_is_user_disabled(string2str(df->template))) {
nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: cannot enable a job of a disabled template: %s", rfe->function);
nd_log(NDLS_DAEMON, NDLP_ERR,
"DYNCFG: cannot enable a job of a disabled template: %s",
rfe->function);

make_the_call_to_plugin = false;
rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: this job belongs to disabled template");
rc = dyncfg_default_response(
rfe->result.wb, HTTP_RESP_BAD_REQUEST,
"dyncfg functions intercept: this job belongs to disabled template");
}
}

Expand All @@ -356,7 +371,7 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __

dyncfg_apply_action_on_all_template_jobs(rfe, id, cmd);

rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_OK, "applied");
rc = dyncfg_default_response(rfe->result.wb, HTTP_RESP_OK, "applied to all template job");
make_the_call_to_plugin = false;
}
else if (cmd == DYNCFG_CMD_SCHEMA) {
Expand Down
36 changes: 33 additions & 3 deletions src/daemon/config/dyncfg-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ static void dyncfg_to_json(DYNCFG *df, const char *id, BUFFER *wb) {
buffer_json_member_add_string(wb, "template", string2str(df->template));

buffer_json_member_add_string(wb, "status", dyncfg_id2status(df->current.status));
dyncfg_cmds2json_array(df->cmds, "cmds", wb);
dyncfg_cmds2json_array(df->current.status == DYNCFG_STATUS_ORPHAN ? DYNCFG_CMD_REMOVE : df->cmds, "cmds", wb);
buffer_json_member_add_object(wb, "access");
{
http_access2buffer_json_array(wb, "view", df->view_access);
Expand Down Expand Up @@ -200,9 +200,39 @@ static int dyncfg_config_execute_cb(struct rrd_function_execute *rfe, void *data
dyncfg_tree_for_host(host, rfe->result.wb, path, id);
}
else {
id = action;
action = path;
path = NULL;

if(id && *id && dyncfg_cmds2id(action) == DYNCFG_CMD_REMOVE) {
const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, id);
if(item) {
DYNCFG *df = dictionary_acquired_item_value(item);

if(!rrd_function_available(host, string2str(df->function)))
df->current.status = DYNCFG_STATUS_ORPHAN;

bool delete = (df->current.status == DYNCFG_STATUS_ORPHAN);
dictionary_acquired_item_release(dyncfg_globals.nodes, item);

if(delete) {
dictionary_del(dyncfg_globals.nodes, id);
dyncfg_file_delete(id);
code = dyncfg_default_response(rfe->result.wb, 200, "");
goto cleanup;
}
}
}

code = HTTP_RESP_NOT_FOUND;
nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: unknown config id '%s' in call: '%s'. This can happen if the plugin that registered the dynamic configuration is not running now.", action, rfe->function);
rrd_call_function_error(rfe->result.wb, "unknown config id given", code);
nd_log(NDLS_DAEMON, NDLP_ERR,
"DYNCFG: unknown config id '%s' in call: '%s'. "
"This can happen if the plugin that registered the dynamic configuration is not running now.",
action, rfe->function);

rrd_call_function_error(
rfe->result.wb,
"unknown config id given", code);
}

cleanup:
Expand Down

0 comments on commit 81cb35d

Please sign in to comment.