This is an automated email from the ASF dual-hosted git repository.
dmeden pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 48092b1106 TS API - records.yaml Add API to parse YAML nodes that are
expected to be read as legacy records. (#9599)
48092b1106 is described below
commit 48092b110660f0415a971e96b59b649b29dd1ba2
Author: Damian Meden <[email protected]>
AuthorDate: Thu Jun 22 12:52:49 2023 +0100
TS API - records.yaml Add API to parse YAML nodes that are expected to be
read as legacy records. (#9599)
* TS API - records.yaml Add API to parse YAML nodes that are expected to
be read as legacy records.
* Wrap internal call with try/catch to avoid handling this back to the TS
API caller
---
.../api/functions/TSRecYAMLConfigParse.en.rst | 150 +++++++++++++++++++++
include/ts/apidefs.h.in | 11 ++
include/ts/ts.h | 23 ++++
src/traffic_server/InkAPI.cc | 33 ++++-
4 files changed, 216 insertions(+), 1 deletion(-)
diff --git a/doc/developer-guide/api/functions/TSRecYAMLConfigParse.en.rst
b/doc/developer-guide/api/functions/TSRecYAMLConfigParse.en.rst
new file mode 100644
index 0000000000..a4784f50f9
--- /dev/null
+++ b/doc/developer-guide/api/functions/TSRecYAMLConfigParse.en.rst
@@ -0,0 +1,150 @@
+.. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+.. include:: ../../../common.defs
+
+.. default-domain:: c
+
+TSYAMLRecCfgFieldData
+*********************
+
+Parse a record yaml file.
+
+Synopsis
+========
+
+.. code-block:: cpp
+
+ #include <ts/ts.h>
+ #include <ts/apidefs.h>
+
+.. type:: TSYAMLRecCfgFieldData
+
+ Configuration field info.
+
+.. type:: TSReturnCode (*TSYAMLRecNodeHandler)(const TSYAMLRecCfgFieldData
*cfg, void *data);
+
+ Record field callback (called on every parsed record field(last node field
on each YAML map))
+
+.. function:: TSReturnCode TSRecYAMLConfigParse(TSYaml node,
TSYAMLRecNodeHandler handler, void *data);
+
+ Parse a YAML node following the record structure. Handler will be called on
every final field.
+
+
+Description
+===========
+
+
+ ``TSYAMLRecCfgFieldData``
+
+ This contains information about the parsed record field. This will be
passed back to the `TSYAMLRecNodeHandler`
+ when you call `TSRecYAMLConfigParse`. This class holds the record name as
well as the YAML node in case the caller
+ wants to use this information to manipulate the record.
+ The record name set is just a reflection of what was build from the YAML
document parsing. This API will not perform any
+ check against the internal ATS records. The record validation should be
done by the caller, for instance, by calling `TSHttpTxnConfigFind`.
+
+.. var:: const char* field_name
+
+ A null-terminated string with the YAML field name. This holds the name of
the scalar field. This is mostly to be used for logging.
+
+.. var:: const char* record_name
+
+ A null-terminated string with the record name which was built when parsing
the YAML file. Example: `proxy.config.diags.debug.enabled`.
+ Use this to validate or do any check base on a record name.
+
+.. var:: TSYaml value_node
+
+ A `YAML::Node` type holding the value of the field. Field value must be
extracted from here. You can use the `YAML::Node` API to deduce
+ the type, etc.
+
+ :c:tye: TSYAMLRecNodeHandler
+
+ Callback function for the caller to deal with each parsed node. ``cfg``
holds the details of the parsed field. `data` can be used to
+ pass information along.
+
+ :c:func: TSRecYAMLConfigParse
+
+ Parse a YAML node following the record structure internals. On every scalar
node the `handler` callback will be
+ invoked with the appropriate parsed fields. `data` can be used to pass
information along to every callback, this could be
+ handy when you need to read/set data inside the `TSYAMLRecNodeHandler` to
be read at a later stage.
+
+
+Example:
+
+ .. code-block:: yaml
+
+ ts:
+ plugin_x:
+ field_1: 1
+ data:
+ data_field_1: XYZ
+
+
+ `TSRecYAMLConfigParse` will parse the node and will call back on each field
(`field_1` and `data_field_1`)
+
+
+ A coding example using the above yaml file would look like:
+
+
+ .. code-block:: cpp
+
+ TSReturnCode
+ field_handler(const TSYAMLRecCfgFieldData *cfg, void *data/*optional*/)
+ {
+ YAML::Node value = *reinterpret_cast<YAML::Node *>(cfg->value_node);
+
+ /*
+ Th callback function will be invoked twice:
+
+ 1:
+ cfg->field_name = field_1
+ cfg->record_name = ts.plugin_x.field_1
+
+ 2:
+ cfg->field_name = data_field_1
+ cfg->record_name = ts.plugin_x.data.data_field_1
+
+
+ If we need to check the type, we either get the type from the
YAML::Node::Tag if set, or from @c TSHttpTxnConfigFind
+ */
+
+ return TS_SUCCESS;
+ }
+
+ void my_plugin_function_to_parse_a_records_like_yaml_file(const char*
path) {
+ try {
+ YAML::Node root = YAML::LoadFile(path);
+
+ auto ret = TSRecYAMLConfigParse(reinterpret_cast<TSYaml>(&root),
field_handler, nullptr);
+ if (ret != TS_SUCCESS) {
+ TSError("We found an error while parsing '%s'.", path.c_str());
+ return;
+ }
+ } catch(YAML::Exception const&ex) {
+ // :(
+ }
+ }
+ ..
+
+
+Return Values
+=============
+
+:c:func:`TSRecYAMLConfigParse`
+
+:c:func:`TSRecYAMLConfigParse` This will return :const:`TS_ERROR` if there
was an issue while parsing the file. Particular node errors
+should be handled by the `TSYAMLRecNodeHandler` implementation.
+
diff --git a/include/ts/apidefs.h.in b/include/ts/apidefs.h.in
index e725ff9958..89db42801d 100644
--- a/include/ts/apidefs.h.in
+++ b/include/ts/apidefs.h.in
@@ -1490,6 +1490,17 @@ typedef struct TSRPCHandlerOptions_s {
} auth;
} TSRPCHandlerOptions;
+/// Configuration field info related to a parsed YAML node.
+///
+/// This contains information about the parsed record field. This will be
passed back to the @a TSYAMLRecNodeHandler
+/// when you call @a TSRecYAMLConfigParse. This class holds the record name as
well as the YAML node in case the caller
+/// wants to use this information to manipulate the record.
+typedef struct TSYAMLCfgFieldData_s {
+ const char *field_name; ///< YAML field name. null terminated
+ const char *record_name; ///< record name. null terminated
+ TSYaml value_node; ///< YAML::Node pointer.
+} TSYAMLRecCfgFieldData;
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/include/ts/ts.h b/include/ts/ts.h
index cfe7479056..b612f6b9ef 100644
--- a/include/ts/ts.h
+++ b/include/ts/ts.h
@@ -2123,6 +2123,29 @@ tsapi void TSStatIntSet(int the_stat, TSMgmtInt value);
tsapi TSReturnCode TSStatFindName(const char *name, int *idp);
+/**
+ Records.yaml file handling API.
+
+ If you need to parse a records.yaml file and need to handle each node
separately then
+ this API should be used, an example of this would be the conf_remap plugin.
+
+ TSYAMLRecNodeHandler
+
+ Callback function for the caller to deal with each parsed node. ``cfg``
holds
+ the details of the parsed field. `data` can be used to pass information
along.
+*/
+typedef TSReturnCode (*TSYAMLRecNodeHandler)(const TSYAMLRecCfgFieldData *cfg,
void *data);
+/**
+ Parse a YAML node following the record structure internals. On every scalar
node
+ the @a handler callback will be invoked with the appropriate parsed fields.
@a data
+ can be used to pass information along to every callback, this could be
handy when
+ you need to read/set data inside the @c TSYAMLRecNodeHandler to be read at
a later stage.
+
+ This will return TS_ERROR if there was an issue while parsing the file.
Particular node errors
+ should be handled by the @c TSYAMLRecNodeHandler implementation.
+*/
+tsapi TSReturnCode TSRecYAMLConfigParse(TSYaml node, TSYAMLRecNodeHandler
handler, void *data);
+
/* --------------------------------------------------------------------------
tracing api */
diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc
index 8514653d20..967dc33165 100644
--- a/src/traffic_server/InkAPI.cc
+++ b/src/traffic_server/InkAPI.cc
@@ -73,12 +73,13 @@
#include "records/I_RecordsConfig.h"
#include "records/I_RecDefs.h"
#include "records/I_RecCore.h"
+#include "records/RecYAMLDecoder.h"
#include "I_Machine.h"
#include "HttpProxyServerMain.h"
#include "shared/overridable_txn_vars.h"
#include "rpc/jsonrpc/JsonRPC.h"
-
+#include <swoc/bwf_base.h>
#include "ts/ts.h"
/****************************************************************
@@ -10268,3 +10269,33 @@ TSRPCHandlerError(int ec, const char *descr, size_t
descr_len)
Debug("rpc.api", ">> error flagged.");
return TS_SUCCESS;
}
+
+TSReturnCode
+TSRecYAMLConfigParse(TSYaml node, TSYAMLRecNodeHandler handler, void *data)
+{
+ swoc::Errata err;
+ try {
+ err = ParseRecordsFromYAML(
+ *reinterpret_cast<YAML::Node *>(node),
+ [handler, data](const CfgNode &field, swoc::Errata &) -> void {
+ // Errors from the handler should be reported and handled by the
handler.
+ // RecYAMLConfigFileParse will report any YAML parsing error.
+ TSYAMLRecCfgFieldData cfg;
+ auto const &field_str = field.node.as<std::string>();
+ cfg.field_name = field_str.c_str();
+ cfg.record_name = field.get_record_name().data();
+ cfg.value_node = reinterpret_cast<TSYaml>(const_cast<YAML::Node
*>(&field.value_node));
+ handler(&cfg, data);
+ },
+ true /* lock */);
+ } catch (std::exception const &ex) {
+ err.note(ERRATA_ERROR, "RecYAMLConfigParse error cought: {}", ex.what());
+ }
+ // Drop API logs in case of an error.
+ if (!err.empty()) {
+ std::string buf;
+ Debug("plugin", "%s", swoc::bwprint(buf, "{}", err).c_str());
+ }
+
+ return err.empty() ? TS_SUCCESS : TS_ERROR;
+}