Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions doc/appendices/command-line/traffic_ctl.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,60 @@ Display the current value of a configuration record.
max: !<tag:yaml.org,2002:int> 100


.. program:: traffic_ctl config
.. option:: reset PATH [PATH ...]

Reset configuration record(s) to their default values. The PATH argument is used as a
regex pattern to match against record names. Multiple paths at once can be provided.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not really a regex pattern right? It's just prefix of the config. Can users put a full regex expression in the argument?


- ``records`` - Reset all configuration records to defaults
- A partial path like ``proxy.config.http`` or ``records.http`` - Reset all records matching the pattern
- A full record name like ``proxy.config.diags.debug.enabled`` - Reset a specific record

**Path Format Support**

Both record name format and YAML format are supported. Paths starting with ``records.``
are automatically converted to ``proxy.config.`` before matching:

====================================== ======================================
YAML Format Record Name Format
====================================== ======================================
``records.http`` ``proxy.config.http``
``records.diags.debug.enabled`` ``proxy.config.diags.debug.enabled``
``records.cache.ram_cache.size`` ``proxy.config.cache.ram_cache.size``
====================================== ======================================

This allows you to use the same path style as in :file:`records.yaml` configuration files.

Examples:

Reset all records to defaults:

.. code-block:: bash

$ traffic_ctl config reset records

Reset all HTTP configuration records (both formats are equivalent):

.. code-block:: bash

$ traffic_ctl config reset proxy.config.http
$ traffic_ctl config reset records.http

Reset a specific record:

.. code-block:: bash

$ traffic_ctl config reset proxy.config.diags.debug.enabled

Using YAML-style path for the same record:

.. code-block:: bash

$ traffic_ctl config reset records.diags.debug.enabled



.. program:: traffic_ctl config
.. option:: status

Expand Down
1 change: 1 addition & 0 deletions src/mgmt/rpc/handlers/config/Configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -206,4 +206,5 @@ reload_config(std::string_view const & /* id ATS_UNUSED */, YAML::Node const & /

return resp;
}

} // namespace rpc::handlers::config
61 changes: 61 additions & 0 deletions src/traffic_ctl/CtrlCommands.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ const StringToFormatFlagsMap _Fmt_str_to_enum = {
{"json", BasePrinter::Options::FormatFlags::JSON},
{"rpc", BasePrinter::Options::FormatFlags::RPC }
};

constexpr std::string_view YAML_PREFIX{"records."};
constexpr std::string_view RECORD_PREFIX{"proxy.config."};

/// Convert YAML-style path (records.diags.debug) to record name format (proxy.config.diags.debug).
/// If the path doesn't start with "records.", it's returned unchanged.
std::string
yaml_to_record_name(std::string_view path)
{
swoc::TextView tv{path};
if (tv.starts_with(YAML_PREFIX)) {
return std::string{RECORD_PREFIX} + std::string{path.substr(YAML_PREFIX.size())};
}
return std::string{path};
}
} // namespace

BasePrinter::Options::FormatFlags
Expand Down Expand Up @@ -142,6 +157,9 @@ ConfigCommand::ConfigCommand(ts::Arguments *args) : RecordCommand(args)
} else if (args->get(SET_STR)) {
_printer = std::make_unique<ConfigSetPrinter>(printOpts);
_invoked_func = [&]() { config_set(); };
} else if (args->get(RESET_STR)) {
_printer = std::make_unique<ConfigSetPrinter>(printOpts);
_invoked_func = [&]() { config_reset(); };
} else if (args->get(STATUS_STR)) {
_printer = std::make_unique<ConfigStatusPrinter>(printOpts);
_invoked_func = [&]() { config_status(); };
Expand Down Expand Up @@ -237,6 +255,49 @@ ConfigCommand::config_set()
_printer->write_output(response);
}

void
ConfigCommand::config_reset()
{
auto const &paths = get_parsed_arguments()->get(RESET_STR);

// Build lookup request - always use REGEX to support partial path matching
shared::rpc::RecordLookupRequest lookup_request;

if (paths.empty() || (paths.size() == 1 && paths[0] == "records")) {
lookup_request.emplace_rec(".*", shared::rpc::REGEX, shared::rpc::CONFIG_REC_TYPES);
} else {
for (auto const &path : paths) {
// Convert YAML-style path (records.*) to record name format (proxy.config.*)
auto record_path = yaml_to_record_name(path);
lookup_request.emplace_rec(record_path, shared::rpc::REGEX, shared::rpc::CONFIG_REC_TYPES);
}
}

// Lookup matching records
auto lookup_response = invoke_rpc(lookup_request);
if (lookup_response.is_error()) {
_printer->write_output(lookup_response);
return;
}

// Build reset request from modified records (current != default)
auto const &records = lookup_response.result.as<shared::rpc::RecordLookUpResponse>();
ConfigSetRecordRequest set_request;

for (auto const &rec : records.recordList) {
if (rec.currentValue != rec.defaultValue) {
set_request.params.push_back(ConfigSetRecordRequest::Params{rec.name, rec.defaultValue});
}
}

if (set_request.params.size() == 0) {
std::cout << "No records to reset (all matching records are already at default values)\n";
return;
}

_printer->write_output(invoke_rpc(set_request));
}

void
ConfigCommand::config_reload()
{
Expand Down
2 changes: 2 additions & 0 deletions src/traffic_ctl/CtrlCommands.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ class ConfigCommand : public RecordCommand
static inline const std::string DIFF_STR{"diff"};
static inline const std::string DEFAULTS_STR{"defaults"};
static inline const std::string SET_STR{"set"};
static inline const std::string RESET_STR{"reset"};
static inline const std::string COLD_STR{"cold"};
static inline const std::string APPEND_STR{"append"};
static inline const std::string STATUS_STR{"status"};
Expand All @@ -141,6 +142,7 @@ class ConfigCommand : public RecordCommand
void config_diff();
void config_status();
void config_set();
void config_reset();
void file_config_set();
void config_reload();
void config_show_file_registry();
Expand Down
6 changes: 6 additions & 0 deletions src/traffic_ctl/traffic_ctl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ main([[maybe_unused]] int argc, const char **argv)
"Add type tag to the yaml field. This is needed if the record is not registered inside ATS. [only relevant if --cold set]",
"", 1)
.add_example_usage("traffic_ctl config set RECORD VALUE");
config_command
.add_command("reset", "Reset configuration values matching a path pattern to their defaults", "", MORE_THAN_ZERO_ARG_N,
Command_Execute)
.add_example_usage("traffic_ctl config reset records")
.add_example_usage("traffic_ctl config reset proxy.config.http")
.add_example_usage("traffic_ctl config reset proxy.config.http.cache_enabled");

config_command.add_command("registry", "Show configuration file registry", Command_Execute)
.add_example_usage("traffic_ctl config registry");
Expand Down
74 changes: 67 additions & 7 deletions tests/gold_tests/traffic_ctl/traffic_ctl_config_output.test.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,27 @@

##### CONFIG GET

# YAML output
# Test 0: YAML output
traffic_ctl.config().get("proxy.config.diags.debug.tags").as_records().validate_with_goldfile("t1_yaml.gold")
# Default output
# Test 1: Default output
traffic_ctl.config().get("proxy.config.diags.debug.enabled").validate_with_text("proxy.config.diags.debug.enabled: 1")
# Default output with default.
# Test 2: Default output with default.
traffic_ctl.config().get("proxy.config.diags.debug.tags").with_default() \
.validate_with_text("proxy.config.diags.debug.tags: rpc # default http|dns")

# Now same output test but with defaults, traffic_ctl supports adding default value
# Test 3: Now same output test but with defaults, traffic_ctl supports adding default value
# when using --records.
traffic_ctl.config().get("proxy.config.diags.debug.tags").as_records().with_default().validate_with_goldfile("t2_yaml.gold")
# Test 4:
traffic_ctl.config().get(
"proxy.config.diags.debug.tags proxy.config.diags.debug.enabled proxy.config.diags.debug.throttling_interval_msec").as_records(
).with_default().validate_with_goldfile("t3_yaml.gold")

##### CONFIG MATCH
# Test 5:
traffic_ctl.config().match("threads").with_default().validate_with_goldfile("match.gold")

# The idea is to check the traffic_ctl yaml emitter when a value starts with the
# Test 6: The idea is to check the traffic_ctl yaml emitter when a value starts with the
# same prefix of a node like:
# diags:
# logfile:
Expand All @@ -70,12 +72,70 @@
traffic_ctl.config().match("diags.logfile").as_records().validate_with_goldfile("t4_yaml.gold")

##### CONFIG DIFF
# Test 7:
traffic_ctl.config().diff().validate_with_goldfile("diff.gold")
# Test 8:
traffic_ctl.config().diff().as_records().validate_with_goldfile("diff_yaml.gold")

##### CONFIG DESCRIBE
# don't really care about values, but just output and that the command actually went through
# Test 9: don't really care about values, but just output and that the command actually went through
traffic_ctl.config().describe("proxy.config.http.server_ports").validate_with_goldfile("describe.gold")

# Make sure that the command returns an exit code of 2
##### CONFIG RESET
# Test 10: Reset a single modified record (proxy.config.diags.debug.tags is set to "rpc" in records_yaml,
# default is "http|dns", so it should be reset)
traffic_ctl.config().reset("proxy.config.diags.debug.tags").validate_with_text(
"Set proxy.config.diags.debug.tags, please wait 10 seconds for traffic server to sync "
"configuration, restart is not required")
# Test 11: Validate the record was reset to its default value
traffic_ctl.config().get("proxy.config.diags.debug.tags").validate_with_text("proxy.config.diags.debug.tags: http|dns")

# Test 12: Reset records matching a partial path (proxy.config.diags)
# First set the record back to non-default for this test
traffic_ctl.config().set("proxy.config.diags.debug.tags", "rpc").exec()
# Test 13: Resetting proxy.config.diags should reset all matching modified records under that path
traffic_ctl.config().reset("proxy.config.diags").validate_contains_all(
"Set proxy.config.diags.debug.tags", "Set proxy.config.diags.debug.enabled")
# Test 14: Validate the record was reset to its default value
traffic_ctl.config().get("proxy.config.diags.debug.tags").validate_with_text("proxy.config.diags.debug.tags: http|dns")

# Test 15: Reset all records using "records" keyword
# First set the record back to non-default for this test
traffic_ctl.config().set("proxy.config.diags.debug.tags", "rpc").exec()
# Test 16: This will reset all modified records (including proxy.config.diags.debug.tags)
# Some may require restart, which is ok, we can use diff anyways as the records that needs
# restart will just change the value but won't have any effect.
traffic_ctl.config().reset("records").exec()
# Validate the diff
# Test 17: Validate the diff
traffic_ctl.config().diff().validate_with_text("")
# Test 18: Validate the record was reset to its default value
traffic_ctl.config().get("proxy.config.diags.debug.tags").validate_with_text("proxy.config.diags.debug.tags: http|dns")

# # Test resetting when no records need resetting (all already at default)
# # Create a new instance with default values only
# traffic_ctl_default = Make_traffic_ctl(Test, None)
# traffic_ctl_default.config().reset("proxy.config.diags.debug.enabled").validate_with_text(
# "No records to reset (all matching records are already at default values)")

##### CONFIG RESET with YAML-style paths (records.* format)
# Test 19: Set a record to non-default first
traffic_ctl.config().set("proxy.config.diags.debug.tags", "yaml_test").exec()
# Test 20: Reset using YAML-style path (records.diags.debug.tags instead of proxy.config.diags.debug.tags)
traffic_ctl.config().reset("records.diags.debug.tags").validate_with_text(
"Set proxy.config.diags.debug.tags, please wait 10 seconds for traffic server to sync "
"configuration, restart is not required")
# Test 21: Validate the record was reset to its default value
traffic_ctl.config().get("proxy.config.diags.debug.tags").validate_with_text("proxy.config.diags.debug.tags: http|dns")

# Test 22: Reset using YAML-style partial path (records.diags)
traffic_ctl.config().set("proxy.config.diags.debug.tags", "yaml_partial_test").exec()
traffic_ctl.config().set("proxy.config.diags.debug.enabled", "1").exec()
# Test 23: Reset using records.diags (YAML format)
traffic_ctl.config().reset("records.diags").validate_contains_all(
"Set proxy.config.diags.debug.tags", "Set proxy.config.diags.debug.enabled")
# Test 24: Validate record was reset
traffic_ctl.config().get("proxy.config.diags.debug.tags").validate_with_text("proxy.config.diags.debug.tags: http|dns")

# Test 25: Make sure that the command returns an exit code of 2
traffic_ctl.config().get("invalid.should.set.the.exit.code.to.2").validate_with_exit_code(2)
Loading