Skip to content

Commit 6846b06

Browse files
authored
records.yaml - Add support to load multiple YAML docs from the same file and let traffic_ctl to modify a records.yaml file (#9404)
* records.yaml - Add support to load multiple YAML docs from the same stream/file. This will work as the legacy records.config where a later variable would override a previous value. * traffic_ctl: Allow config set/get from a yaml configuration file. * DOC: Add supported documentation
1 parent b6d4191 commit 6846b06

17 files changed

+842
-57
lines changed

doc/admin-guide/files/records.yaml.en.rst

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ records.yaml
3131

3232
The :file:`records.yaml` file (by default, located in
3333
``/usr/local/etc/trafficserver/``) is a YAML base configuration file used by
34-
the |TS| software. Many of the fields in :file:`records.yaml` are set
35-
automatically when you set configuration options with :option:`traffic_ctl config set`. After you
36-
modify :file:`records.yaml`, run the command :option:`traffic_ctl config reload`
34+
the |TS| software. After you modify :file:`records.yaml`, run the command :option:`traffic_ctl config reload`
3735
to apply the changes.
3836

3937
.. note::
@@ -47,7 +45,8 @@ to apply the changes.
4745
YAML structure
4846
==============
4947

50-
All fields are located inside the ``ts`` root node.
48+
All fields are located inside the ``ts`` root node. ATS supports reading multiple documents from
49+
the same YAML stream, subsequent documents overrides earlier fields.
5150

5251

5352
.. code-block:: yaml

doc/appendices/command-line/traffic_ctl.en.rst

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,16 +170,17 @@ Display the current value of a configuration record.
170170
.. program:: traffic_ctl config get
171171
.. option:: --records
172172

173-
If this flag is provided, :option:`traffic_ctl config get` will emit results in
174-
:file:`records.yaml` format.
173+
If this flag is provided, :option:`traffic_ctl config get` will emit results in internal ats variable format.
174+
175+
The option :ref:`--cold <traffic_ctl_config_cold>` is available to get the values from a file.
175176

176177
.. program:: traffic_ctl config
177178
.. option:: match [--records] REGEX [REGEX...]
178179

179180
:ref:`admin_lookup_records`
180181

181182
Display the current values of all configuration variables whose names match the given regular
182-
expression. The ``--records`` flag has the same behavior as :option:`traffic_ctl config get
183+
expression. The ``--records`` flag has the same behavior as `traffic_ctl config get
183184
--records`.
184185

185186
.. program:: traffic_ctl config
@@ -203,6 +204,77 @@ Display the current value of a configuration record.
203204
documentation for a list of the configuration variables you can specify. Note that this is not a
204205
synchronous operation.
205206

207+
Supports the following options.
208+
209+
210+
.. _traffic_ctl_config_cold:
211+
212+
.. option:: --cold, -c [filename]
213+
214+
215+
This option indicates to `traffic_ctl` that the action should be performed on a configuration file instead of using the ATS RPC
216+
facility to store the new value. `traffic_ctl` will save the value in the passed `filename`, if no `filename` passed, then the sysconfig
217+
:file:`records.yaml` will be attempted to be used.
218+
219+
ATS supports parsing multiple documents from the same YAML stream, so if you attempt to set a variable on a document with
220+
none, one or multiple documents then a new document will be appended. In case you want to modify an existing field then `-u` option
221+
should be passed, so the latest(top to bottom) field will be modified, if there is no variable already set in any of the documents,
222+
then the new variable will be set in the latest document of the stream.
223+
224+
Specifying the file name is not needed as `traffic_ctl` will try to use the build(or the runroot if used) information to figure
225+
out the path to the `records.yaml`.
226+
227+
If the file exists and is empty a new document will be created. If a file does not exist, an attempt to create a new file will be done.
228+
229+
This option(only for the config file changes) lets you use the prefix `proxy.config.` or `ts.` for variable names, either would work.
230+
If different prefix name is prepend, then traffic_ctl will work away using the provided variable name, this may not be what is intended
231+
so, make sure you use the right prefixes.
232+
233+
234+
Appending a new field in a records.yaml file.
235+
236+
.. code-block:: bash
237+
238+
$ traffic_ctl config set proxy.config.diags.debug.enabled 1 -c records.yaml
239+
$ cat records.yaml
240+
ts:
241+
...
242+
# Document modified by traffic_ctl Mon Feb 13 23:07:15 2023
243+
#
244+
---
245+
ts:
246+
diags:
247+
debug:
248+
enabled: 1
249+
250+
.. note::
251+
252+
The following options are only considered if ``--cold, -c`` is used, ignored otherwise.
253+
254+
.. option:: --update -u
255+
256+
Update latest field present. If there is no variable already set in any of the documents, then the new variable will be set in
257+
the latest document.
258+
259+
.. option:: --type, -t int | float | str
260+
261+
Inject a tag information on the modified/new field, this is useful when you set a non registered record inside ATS.
262+
263+
.. code-block:: bash
264+
265+
$ traffic_ctl config set ts.some.plugin.config.max 100 -t int -c records.yaml
266+
$ cat records.yaml
267+
...
268+
# Document modified by traffic_ctl Mon Feb 13 23:07:15 2023
269+
#
270+
---
271+
ts:
272+
some:
273+
plugin:
274+
config:
275+
max: !<tag:yaml.org,2002:int> 100
276+
277+
206278
.. program:: traffic_ctl config
207279
.. option:: status
208280

src/records/RecConfigParse.cc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,14 @@ swoc::Errata
268268
RecYAMLConfigFileParse(const char *path, RecYAMLNodeHandler handler)
269269
{
270270
try {
271-
auto root = YAML::LoadFile(path);
272-
if (auto ret = ParseRecordsFromYAML(root, handler); !ret.empty()) {
273-
return ret;
271+
auto nodes = YAML::LoadAllFromFile(path);
272+
// We will parse each doc from the stream, subsequently config node will overwrite
273+
// the value for previously loaded records. This would work similar to the old
274+
// records.config which a later CONFIG variable would overwrite a previous one.
275+
for (auto const &root : nodes) {
276+
if (auto ret = ParseRecordsFromYAML(root, handler); !ret.empty()) {
277+
return ret;
278+
}
274279
}
275280
} catch (std::exception const &ex) {
276281
return swoc::Errata(ERRATA_ERROR, "Error parsing {}. {}", path, ex.what());

src/traffic_ctl/CtrlCommands.cc

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,16 @@
2424
#include "CtrlCommands.h"
2525
#include "jsonrpc/CtrlRPCRequests.h"
2626
#include "jsonrpc/ctrl_yaml_codecs.h"
27+
#include "utils/yaml_utils.h"
28+
#include "swoc/TextView.h"
29+
#include "swoc/BufferWriter.h"
30+
#include "swoc/bwf_base.h"
31+
2732
namespace
2833
{
2934
/// We use yamlcpp as codec implementation.
3035
using Codec = yamlcpp_json_emitter;
31-
36+
} // namespace
3237
const std::unordered_map<std::string_view, BasePrinter::Options::OutputFormat> _Fmt_str_to_enum = {
3338
{"pretty", BasePrinter::Options::OutputFormat::PRETTY},
3439
{"legacy", BasePrinter::Options::OutputFormat::LEGACY},
@@ -59,7 +64,6 @@ parse_print_opts(ts::Arguments *args)
5964
{
6065
return {parse_format(args)};
6166
}
62-
} // namespace
6367

6468
//------------------------------------------------------------------------------------------------------------------------------------
6569
CtrlCommand::CtrlCommand(ts::Arguments *args) : _arguments(args) {}
@@ -75,7 +79,7 @@ CtrlCommand::execute()
7579
}
7680

7781
std::string
78-
CtrlCommand::invoke_rpc(std::string const &request)
82+
RPCAccessor::invoke_rpc(std::string const &request)
7983
{
8084
if (_printer->print_rpc_message()) {
8185
std::string text;
@@ -96,15 +100,15 @@ CtrlCommand::invoke_rpc(std::string const &request)
96100
}
97101

98102
shared::rpc::JSONRPCResponse
99-
CtrlCommand::invoke_rpc(shared::rpc::ClientRequest const &request)
103+
RPCAccessor::invoke_rpc(shared::rpc::ClientRequest const &request)
100104
{
101105
std::string encodedRequest = Codec::encode(request);
102106
std::string resp = invoke_rpc(encodedRequest);
103107
return Codec::decode(resp);
104108
}
105109

106110
void
107-
CtrlCommand::invoke_rpc(shared::rpc::ClientRequest const &request, std::string &resp)
111+
RPCAccessor::invoke_rpc(shared::rpc::ClientRequest const &request, std::string &resp)
108112
{
109113
std::string encodedRequest = Codec::encode(request);
110114
resp = invoke_rpc(encodedRequest);
@@ -207,6 +211,7 @@ ConfigCommand::config_set()
207211

208212
_printer->write_output(response);
209213
}
214+
210215
void
211216
ConfigCommand::config_reload()
212217
{

src/traffic_ctl/CtrlCommands.h

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,30 @@ limitations under the License.
2626
#include "shared/rpc/RPCClient.h"
2727
#include "CtrlPrinters.h"
2828

29+
/// @brief Class that provides access to the RPC side of things.
30+
struct RPCAccessor {
31+
protected:
32+
/// @brief Invoke the remote server. This is the very basic function which does not play or interact with any codec. Request
33+
/// and message should be already en|de coded.
34+
/// @param request A string representation of the json/yaml request.
35+
/// @return a string with the json/yaml response.
36+
/// @note This function does print the raw string if requested by the "--format". No printer involved, standard output.
37+
std::string invoke_rpc(std::string const &request);
38+
39+
/// @brief Function that calls the rpc server. This function takes a json objects and uses the defined coded to convert them to a
40+
/// string. This function will call invoke_rpc(string) overload.
41+
/// @param A Client request.
42+
/// @return A server response.
43+
shared::rpc::JSONRPCResponse invoke_rpc(shared::rpc::ClientRequest const &request);
44+
45+
/// @brief Function that calls the rpc server. The response will not be decoded, it will be a raw string.
46+
void invoke_rpc(shared::rpc::ClientRequest const &request, std::string &bw);
47+
48+
std::unique_ptr<BasePrinter> _printer; //!< Specific output formatter. This should be created by the derived class.
49+
private:
50+
shared::rpc::RPCClient _rpcClient; //!< RPC socket client implementation.
51+
};
52+
2953
// ----------------------------------------------------------------------------------------------------------------------------------
3054
///
3155
/// @brief Base Control Command class.
@@ -49,24 +73,6 @@ class CtrlCommand
4973
virtual void execute();
5074

5175
protected:
52-
/// @brief Invoke the remote server. This is the very basic function which does not play or interact with any codec. Request
53-
/// and message should be already en|de coded.
54-
/// @param request A string representation of the json/yaml request.
55-
/// @return a string with the json/yaml response.
56-
/// @note This function does print the raw string if requested by the "--format". No printer involved, standard output.
57-
std::string invoke_rpc(std::string const &request);
58-
59-
/// @brief Function that calls the rpc server. This function takes a json objects and uses the defined coded to convert them to a
60-
/// string. This function will call invoke_rpc(string) overload.
61-
/// @param A Client request.
62-
/// @return A server response.
63-
shared::rpc::JSONRPCResponse invoke_rpc(shared::rpc::ClientRequest const &request);
64-
65-
/// @brief Function that calls the rpc server. The response will not be decoded, it will be a raw string.
66-
void invoke_rpc(shared::rpc::ClientRequest const &request, std::string &bw);
67-
68-
std::unique_ptr<BasePrinter> _printer; //!< Specific output formatter. This should be created by the derived class.
69-
7076
/// @brief The whole design is that the command will execute the @c _invoked_func once invoked. This function ptr should be
7177
/// set by the appropriated derived class base on the passed parameters. The derived class have the option to override
7278
/// the execute() function and do something else. Check @c RecordCommand as an example.
@@ -81,15 +87,14 @@ class CtrlCommand
8187

8288
private:
8389
ts::Arguments *_arguments = nullptr; //!< parsed traffic_ctl arguments.
84-
shared::rpc::RPCClient _rpcClient; //!< RPC socket client implementation.
8590
};
8691

8792
// -----------------------------------------------------------------------------------------------------------------------------------
8893
///
8994
/// @brief Record Command Implementation
9095
/// Used as base class for any command that needs to access to a TS record.
9196
/// If deriving from this class, make sure you implement @c execute_subcommand() and call the _invoked_func yourself.
92-
class RecordCommand : public CtrlCommand
97+
class RecordCommand : public CtrlCommand, public RPCAccessor
9398
{
9499
public:
95100
using CtrlCommand::CtrlCommand;
@@ -114,6 +119,8 @@ class ConfigCommand : public RecordCommand
114119
static inline const std::string DIFF_STR{"diff"};
115120
static inline const std::string DEFAULTS_STR{"defaults"};
116121
static inline const std::string SET_STR{"set"};
122+
static inline const std::string COLD_STR{"cold"};
123+
static inline const std::string APPEND_STR{"append"};
117124
static inline const std::string STATUS_STR{"status"};
118125
static inline const std::string RELOAD_STR{"reload"};
119126
static inline const std::string REGISTRY_STR{"registry"};
@@ -125,6 +132,7 @@ class ConfigCommand : public RecordCommand
125132
void config_diff();
126133
void config_status();
127134
void config_set();
135+
void file_config_set();
128136
void config_reload();
129137
void config_show_file_registry();
130138

@@ -147,7 +155,7 @@ class MetricCommand : public RecordCommand
147155
MetricCommand(ts::Arguments *args);
148156
};
149157
// -----------------------------------------------------------------------------------------------------------------------------------
150-
class HostCommand : public CtrlCommand
158+
class HostCommand : public CtrlCommand, public RPCAccessor
151159
{
152160
public:
153161
HostCommand(ts::Arguments *args);
@@ -163,7 +171,7 @@ class HostCommand : public CtrlCommand
163171
void status_up();
164172
};
165173
// -----------------------------------------------------------------------------------------------------------------------------------
166-
class PluginCommand : public CtrlCommand
174+
class PluginCommand : public CtrlCommand, public RPCAccessor
167175
{
168176
public:
169177
PluginCommand(ts::Arguments *args);
@@ -173,7 +181,7 @@ class PluginCommand : public CtrlCommand
173181
void plugin_msg();
174182
};
175183
// -----------------------------------------------------------------------------------------------------------------------------------
176-
class DirectRPCCommand : public CtrlCommand
184+
class DirectRPCCommand : public CtrlCommand, public RPCAccessor
177185
{
178186
public:
179187
DirectRPCCommand(ts::Arguments *args);
@@ -194,7 +202,7 @@ class DirectRPCCommand : public CtrlCommand
194202
bool validate_input(std::string const &in) const;
195203
};
196204
// -----------------------------------------------------------------------------------------------------------------------------------
197-
class ServerCommand : public CtrlCommand
205+
class ServerCommand : public CtrlCommand, public RPCAccessor
198206
{
199207
public:
200208
ServerCommand(ts::Arguments *args);
@@ -208,7 +216,7 @@ class ServerCommand : public CtrlCommand
208216
};
209217
//
210218
// -----------------------------------------------------------------------------------------------------------------------------------
211-
struct StorageCommand : public CtrlCommand {
219+
struct StorageCommand : public CtrlCommand, public RPCAccessor {
212220
StorageCommand(ts::Arguments *args);
213221

214222
private:
@@ -219,3 +227,6 @@ struct StorageCommand : public CtrlCommand {
219227
void set_storage_offline();
220228
};
221229
// -----------------------------------------------------------------------------------------------------------------------------------
230+
231+
BasePrinter::Options::OutputFormat parse_format(ts::Arguments *args);
232+
BasePrinter::Options parse_print_opts(ts::Arguments *args);

0 commit comments

Comments
 (0)