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;
+}

Reply via email to