DYNCFG: call the interceptor when a test is made on a new job (#17052)
* call the interceptor when a test is made on a new job * unify exception handling * dont shadow variable * the config tree function, that is also the catch all for all config commands, is now marked async, to allow calling async functions
Costa Tsaousis committed
Feb 25, 2024 at 03:36 UTC
bc3ff3fabefda86d04a327987077cf2c799ecaf2
3 files changed
+41
-9
src/daemon/config/dyncfg-intercept.c
+1
-1
@@ -180,7 +180,7 @@ static int dyncfg_intercept_early_error(struct rrd_function_execute *rfe, int rc
180
return rc;
181
}
182
183
-static const DICTIONARY_ITEM *dyncfg_get_template_of_new_job(const char *job_id) {
183
+const DICTIONARY_ITEM *dyncfg_get_template_of_new_job(const char *job_id) {
184
char id_copy[strlen(job_id) + 1];
185
memcpy(id_copy, job_id, sizeof(id_copy));
186
src/daemon/config/dyncfg-internals.h
+2
@@ -76,6 +76,8 @@ const DICTIONARY_ITEM *dyncfg_add_internal(RRDHOST *host, const char *id, const
76
int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data);
77
void dyncfg_cleanup(DYNCFG *v);
78
79
+const DICTIONARY_ITEM *dyncfg_get_template_of_new_job(const char *job_id);
80
+
81
bool dyncfg_is_user_disabled(const char *id);
82
83
RRDHOST *dyncfg_rrdhost_by_uuid(UUID *uuid);
src/daemon/config/dyncfg-tree.c
+38
-8
@@ -204,31 +204,57 @@ static int dyncfg_config_execute_cb(struct rrd_function_execute *rfe, void *data
204
action = path;
205
path = NULL;
206
207
- if(id && *id && dyncfg_cmds2id(action) == DYNCFG_CMD_REMOVE) {
208
- const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, id);
209
- if(item) {
210
- DYNCFG *df = dictionary_acquired_item_value(item);
207
+ DYNCFG_CMDS cmd = dyncfg_cmds2id(action);
208
+ const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(dyncfg_globals.nodes, id);
209
+ if(!item)
210
+ item = dyncfg_get_template_of_new_job(id);
211
212
- if(!rrd_function_available(host, string2str(df->function)))
213
- df->current.status = DYNCFG_STATUS_ORPHAN;
212
+ if(item) {
213
+ DYNCFG *df = dictionary_acquired_item_value(item);
214
215
+ if(!rrd_function_available(host, string2str(df->function)))
216
+ df->current.status = DYNCFG_STATUS_ORPHAN;
217
+
218
+ if(cmd == DYNCFG_CMD_REMOVE) {
219
bool delete = (df->current.status == DYNCFG_STATUS_ORPHAN);
220
dictionary_acquired_item_release(dyncfg_globals.nodes, item);
221
+ item = NULL;
222
223
if(delete) {
224
+ if(!http_access_user_has_enough_access_level_for_endpoint(rfe->user_access, df->edit_access)) {
225
+ code = dyncfg_default_response(
226
+ rfe->result.wb, HTTP_RESP_FORBIDDEN,
227
+ "dyncfg: you don't have enough edit permissions to execute this command");
228
+ goto cleanup;
229
+ }
230
+
231
dictionary_del(dyncfg_globals.nodes, id);
232
dyncfg_file_delete(id);
233
code = dyncfg_default_response(rfe->result.wb, 200, "");
234
goto cleanup;
235
}
236
}
237
+ else if(cmd == DYNCFG_CMD_TEST && df->type == DYNCFG_TYPE_TEMPLATE && df->current.status != DYNCFG_STATUS_ORPHAN) {
238
+ const char *old_rfe_function = rfe->function;
239
+ char buf2[2048];
240
+ snprintfz(buf2, sizeof(buf2), "config %s %s", dictionary_acquired_item_name(item), action);
241
+ rfe->function = buf2;
242
+ dictionary_acquired_item_release(dyncfg_globals.nodes, item);
243
+ item = NULL;
244
+ code = dyncfg_function_intercept_cb(rfe, data);
245
+ rfe->function = old_rfe_function;
246
+ return code;
247
+ }
248
+
249
+ if(item)
250
+ dictionary_acquired_item_release(dyncfg_globals.nodes, item);
251
}
252
253
code = HTTP_RESP_NOT_FOUND;
254
nd_log(NDLS_DAEMON, NDLP_ERR,
255
"DYNCFG: unknown config id '%s' in call: '%s'. "
256
"This can happen if the plugin that registered the dynamic configuration is not running now.",
231
- action, rfe->function);
257
+ id, rfe->function);
258
259
rrd_call_function_error(
260
rfe->result.wb,
@@ -248,7 +274,11 @@ cleanup:
274
// for which there is no id overloaded.
275
276
void dyncfg_host_init(RRDHOST *host) {
277
+ // IMPORTANT:
278
+ // This function needs to be async, although it is internal.
279
+ // The reason is that it can call by itself another function that may or may not be internal (sync).
280
+
281
rrd_function_add(host, NULL, PLUGINSD_FUNCTION_CONFIG, 120,
282
1000, "Dynamic configuration", "config", HTTP_ACCESS_ANONYMOUS_DATA,
253
- true, dyncfg_config_execute_cb, host);
283
+ false, dyncfg_config_execute_cb, host);
284
}