diff --git a/doc/appendices/command-line/traffic_ctl_jsonrpc.en.rst b/doc/appendices/command-line/traffic_ctl_jsonrpc.en.rst index 6b165b23a4b..ebb91cc65b2 100644 --- a/doc/appendices/command-line/traffic_ctl_jsonrpc.en.rst +++ b/doc/appendices/command-line/traffic_ctl_jsonrpc.en.rst @@ -538,6 +538,42 @@ but rather to the rpc endpoint, so you can directly send requests and receive re } } + +.. option:: invoke + + Invoke a remote call by using the method name as parameter. This could be a handy option if you are developing a new handler or you + just don't want to expose the method in :program:`traffic_ctl`, for instance when implementing a custom handler inside a proprietary plugin. + + .. option:: --params, -p + + Parameters to be passed in the request, YAML or JSON format are accepted. If JSON is passed as param it should not + be mixed with YAML. It's important that you follow the :ref:`jsonrpc-protocol` specs. If the passed param does not + follows the specs the server will reject the request. + +.. _rpc_invoke_example_1: + + Example 1: + + Call a jsonrpc method with no parameter. + + .. code-block:: + + $ traffic_ctl rpc invoke some_jsonrpc_handler + --> {"id": "0dbab88d-b78f-4ebf-8aa3-f100031711a5", "jsonrpc": "2.0", "method": "some_jsonrpc_handler"} + <-- { response } + +.. _rpc_invoke_example_2: + + Example 2: + + Call a jsonrpc method with parameters. + + .. code-block:: + + $ traffic_ctl rpc invoke reload_files_from_folder --params 'filenames: ["file1", "file2"]' 'folder: "/path/to/folder"' + --> {"id": "9ac68652-5133-4d5f-8260-421baca4c67f", "jsonrpc": "2.0", "method": "reload_files_from_folder", "params": {"filenames": ["file1", "file2"], "folder": "/path/to/folder"}} + <-- { response } + Examples ======== diff --git a/doc/developer-guide/jsonrpc/jsonrpc-architecture.en.rst b/doc/developer-guide/jsonrpc/jsonrpc-architecture.en.rst index 511e4e99b75..4025cbd30ff 100644 --- a/doc/developer-guide/jsonrpc/jsonrpc-architecture.en.rst +++ b/doc/developer-guide/jsonrpc/jsonrpc-architecture.en.rst @@ -132,7 +132,6 @@ Please find the `jsonrpc 2.0 request` schema for reference ( `mgmt2/rpc/schema/j * Optional parameters: - * ``params``: A Structured value that holds the parameter values to be used during the invocation of the method. This member diff --git a/include/shared/rpc/RPCRequests.h b/include/shared/rpc/RPCRequests.h index b12c666dcf4..5ab5a7c1d56 100644 --- a/include/shared/rpc/RPCRequests.h +++ b/include/shared/rpc/RPCRequests.h @@ -42,7 +42,7 @@ struct JSONRPCRequest { virtual std::string get_method() const { - return "method"; + return this->method; } }; @@ -225,4 +225,4 @@ operator<<(std::ostream &os, const JSONRPCError &err) return os; } -} // namespace shared::rpc \ No newline at end of file +} // namespace shared::rpc diff --git a/src/traffic_ctl_jsonrpc/CtrlCommands.cc b/src/traffic_ctl_jsonrpc/CtrlCommands.cc index a71af91d534..c64ff0649f2 100644 --- a/src/traffic_ctl_jsonrpc/CtrlCommands.cc +++ b/src/traffic_ctl_jsonrpc/CtrlCommands.cc @@ -379,6 +379,12 @@ DirectRPCCommand::DirectRPCCommand(ts::Arguments args) : CtrlCommand(args) _invoked_func = [&]() { from_file_request(); }; } else if (_arguments.get("input")) { _invoked_func = [&]() { read_from_input(); }; + } else if (_arguments.get("invoke")) { + _invoked_func = [&]() { invoke_method(); }; + if (printOpts._format == BasePrinter::Options::Format::LEGACY) { + // overwrite this and let it drop json instead. + printOpts._format = BasePrinter::Options::Format::DATA_ALL; + } } _printer = std::make_unique(printOpts); @@ -458,6 +464,25 @@ DirectRPCCommand::read_from_input() } } +void +DirectRPCCommand::invoke_method() +{ + shared::rpc::ClientRequest request; + if (auto method = _arguments.get("invoke"); method) { + request.method = method.value(); + // We build up the parameter content if passed. + if (auto params = _arguments.get("params"); params) { + std::ostringstream ss; + for (auto &¶m : _arguments.get("params")) { + ss << param; + ss << '\n'; + } + request.params = YAML::Load(ss.str()); // let if fail if this is bad. + } + _printer->write_output(invoke_rpc(request)); + } +} + //------------------------------------------------------------------------------------------------------------------------------------ ServerCommand::ServerCommand(ts::Arguments args) : CtrlCommand(args) { diff --git a/src/traffic_ctl_jsonrpc/CtrlCommands.h b/src/traffic_ctl_jsonrpc/CtrlCommands.h index 6144dedd709..5c6a7cea392 100644 --- a/src/traffic_ctl_jsonrpc/CtrlCommands.h +++ b/src/traffic_ctl_jsonrpc/CtrlCommands.h @@ -163,6 +163,7 @@ class DirectRPCCommand : public CtrlCommand void from_file_request(); void get_rpc_api(); void read_from_input(); + void invoke_method(); /// run a YAML validation on the input. bool validate_input(std::string const &in) const; }; diff --git a/src/traffic_ctl_jsonrpc/jsonrpc/CtrlRPCRequests.h b/src/traffic_ctl_jsonrpc/jsonrpc/CtrlRPCRequests.h index c3d9740e247..b4466ab5538 100644 --- a/src/traffic_ctl_jsonrpc/jsonrpc/CtrlRPCRequests.h +++ b/src/traffic_ctl_jsonrpc/jsonrpc/CtrlRPCRequests.h @@ -221,17 +221,6 @@ struct ShowRegisterHandlersRequest : shared::rpc::ClientRequest { } }; //------------------------------------------------------------------------------------------------------------------------------------ -// We expect the method to be passed, this request is used to create dynamic requests by using (traffic_ctl rpc invoke "func_name") -struct CustomizableRequest : shared::rpc::ClientRequest { - using super = shared::rpc::ClientRequest; - CustomizableRequest(std::string const &methodName) { super::method = methodName; } - std::string - get_method() const - { - return super::method; - } -}; -//------------------------------------------------------------------------------------------------------------------------------------ /// /// @brief Config status request mapping class. /// diff --git a/src/traffic_ctl_jsonrpc/traffic_ctl.cc b/src/traffic_ctl_jsonrpc/traffic_ctl.cc index 7d7bd4b3bc1..48a017fe938 100644 --- a/src/traffic_ctl_jsonrpc/traffic_ctl.cc +++ b/src/traffic_ctl_jsonrpc/traffic_ctl.cc @@ -160,7 +160,11 @@ main(int argc, const char **argv) "No json/yaml parse validation will take place, the raw content will be directly send to the server.", "", 0, "", "raw") .add_example_usage("traffic_ctl rpc input "); - + direct_rpc_command + .add_command("invoke", "Call a method by using the method name as input parameter", "", MORE_THAN_ONE_ARG_N, + [&]() { command->execute(); }) + .add_option("--params", "-p", "Parameters to be passed in the request, YAML or JSON format", "", MORE_THAN_ONE_ARG_N, "", "") + .add_example_usage("traffic_ctl rpc invoke foo_bar -p \"numbers: [1, 2, 3]\""); try { auto args = parser.parse(argv); argparser_runroot_handler(args.get("run-root").value(), argv[0]);