From 7a7a849fc24261d9036cbf48f14fc40e06d59e24 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 6 Aug 2020 09:58:02 +0930 Subject: [PATCH] lightningd: allow plugin commands and options to mark themselves deprecated. This lets us handle it the same way we handle builtin commands, and also lets us warn if they use deprecated apis and allow-deprecated-apis=false. Signed-off-by: Rusty Russell Changelog-Added: Plugins: can now mark their options and commands deprecated. --- doc/PLUGINS.md | 18 +++++++++++++----- lightningd/plugin.c | 34 +++++++++++++++++++++++++++++++--- lightningd/plugin.h | 1 + 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 9c80101ba..e03b13894 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -58,9 +58,10 @@ interface. ### The `getmanifest` method The `getmanifest` method is required for all plugins and will be -called on startup with optionsl parameters (in particular, it may have -`allow-deprecated-apis: false`, but you should accept others). It -MUST return a JSON object similar to this example: +called on startup with optional parameters (in particular, it may have +`allow-deprecated-apis: false`, but you should accept, and ignore, +other parameters). It MUST return a JSON object similar to this +example: ```json { @@ -69,7 +70,8 @@ MUST return a JSON object similar to this example: "name": "greeting", "type": "string", "default": "World", - "description": "What name should I call you?" + "description": "What name should I call you?", + "deprecated": false } ], "rpcmethods": [ @@ -82,7 +84,8 @@ MUST return a JSON object similar to this example: "name": "gettime", "usage": "", "description": "Returns the current time in {timezone}", - "long_description": "Returns the current time in the timezone that is given as the only parameter.\nThis description may be quite long and is allowed to span multiple lines." + "long_description": "Returns the current time in the timezone that is given as the only parameter.\nThis description may be quite long and is allowed to span multiple lines.", + "deprecated": false } ], "subscriptions": [ @@ -116,6 +119,11 @@ are mandatory, while the `long_description` can be omitted (it'll be set to `description` if it was not provided). `usage` should surround optional parameter names in `[]`. +`options` and `rpcmethods` can mark themselves `deprecated: true` if +you plan on removing them: this will disable them if the user sets +`allow-deprecated-apis` to false (which every developer should do, +right?). + The `dynamic` indicates if the plugin can be managed after `lightningd` has been started. Critical plugins that should not be stopped should set it to false. diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 7b8ae9225..a6e51766a 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -587,6 +587,10 @@ char *plugin_opt_set(const char *arg, struct plugin_opt *popt) char *endp; long long l; + /* Warn them that this is deprecated */ + if (popt->deprecated && !deprecated_apis) + return tal_fmt(tmpctx, "deprecated option (will be removed!)"); + tal_free(popt->value->as_str); popt->value->as_str = tal_strdup(popt, arg); @@ -630,12 +634,13 @@ static void destroy_plugin_opt(struct plugin_opt *opt) static const char *plugin_opt_add(struct plugin *plugin, const char *buffer, const jsmntok_t *opt) { - const jsmntok_t *nametok, *typetok, *defaulttok, *desctok; + const jsmntok_t *nametok, *typetok, *defaulttok, *desctok, *deptok; struct plugin_opt *popt; nametok = json_get_member(buffer, opt, "name"); typetok = json_get_member(buffer, opt, "type"); desctok = json_get_member(buffer, opt, "description"); defaulttok = json_get_member(buffer, opt, "default"); + deptok = json_get_member(buffer, opt, "deprecated"); if (!typetok || !nametok || !desctok) { return tal_fmt(plugin, @@ -648,6 +653,15 @@ static const char *plugin_opt_add(struct plugin *plugin, const char *buffer, popt->name = tal_fmt(popt, "--%.*s", nametok->end - nametok->start, buffer + nametok->start); popt->description = NULL; + if (deptok) { + if (!json_to_bool(buffer, deptok, &popt->deprecated)) + return tal_fmt(plugin, + "%s: invalid \"deprecated\" field %.*s", + popt->name, + deptok->end - deptok->start, + buffer + deptok->start); + } else + popt->deprecated = false; if (json_tok_streq(buffer, typetok, "string")) { popt->type = "string"; @@ -837,7 +851,8 @@ static const char *plugin_rpcmethod_add(struct plugin *plugin, const char *buffer, const jsmntok_t *meth) { - const jsmntok_t *nametok, *categorytok, *desctok, *longdesctok, *usagetok; + const jsmntok_t *nametok, *categorytok, *desctok, *longdesctok, + *usagetok, *deptok; struct json_command *cmd; const char *usage; @@ -846,6 +861,7 @@ static const char *plugin_rpcmethod_add(struct plugin *plugin, desctok = json_get_member(buffer, meth, "description"); longdesctok = json_get_member(buffer, meth, "long_description"); usagetok = json_get_member(buffer, meth, "usage"); + deptok = json_get_member(buffer, meth, "deprecated"); if (!nametok || nametok->type != JSMN_STRING) { return tal_fmt(plugin, @@ -891,7 +907,16 @@ static const char *plugin_rpcmethod_add(struct plugin *plugin, } else usage = "[params]"; - cmd->deprecated = false; + if (deptok) { + if (!json_to_bool(buffer, deptok, &cmd->deprecated)) + return tal_fmt(plugin, + "%s: invalid \"deprecated\" field %.*s", + cmd->name, + deptok->end - deptok->start, + buffer + deptok->start); + } else + cmd->deprecated = false; + cmd->dispatch = plugin_rpcmethod_dispatch; if (!jsonrpc_command_add(plugin->plugins->ld->jsonrpc, cmd, usage)) { return tal_fmt(plugin, @@ -1491,6 +1516,9 @@ void json_add_opt_plugins_array(struct json_stream *response, if (!list_empty(&p->plugin_opts)) { json_object_start(response, "options"); list_for_each(&p->plugin_opts, opt, list) { + if (!deprecated_apis && opt->deprecated) + continue; + /* Trim the `--` that we added before */ opt_name = opt->name + 2; if (opt->value->as_bool) { diff --git a/lightningd/plugin.h b/lightningd/plugin.h index 1e4338f84..60e5fc505 100644 --- a/lightningd/plugin.h +++ b/lightningd/plugin.h @@ -137,6 +137,7 @@ struct plugin_opt { const char *type; const char *description; struct plugin_opt_value *value; + bool deprecated; }; /**