Skip to content

Commit ef2db7f

Browse files
author
Damian Meden
committed
Add inline config field to plugin.yaml
Add the 'config' field to plugin.yaml entries, allowing plugin configuration to be embedded directly using a YAML block scalar (|). The literal text is written to a temporary file at startup and passed to the plugin as an argument. Only scalar values are accepted; structured YAML is rejected to preserve quoting semantics that plugins like txn_box rely on. Made-with: Cursor
1 parent 7906f01 commit ef2db7f

8 files changed

Lines changed: 347 additions & 25 deletions

File tree

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

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,30 @@ configuration entry remains in the file for easy re-enabling.
7171
with ``$`` designate |TS| configuration variables and will be expanded
7272
to their current value before the plugin is loaded.
7373

74+
``config``
75+
----------
76+
77+
**Optional.** Inline configuration content specified as a YAML scalar.
78+
The text is written to a temporary file at startup and the path is
79+
passed to the plugin as an argument, so existing plugins work without
80+
modification.
81+
82+
.. tip::
83+
84+
Use a literal block scalar (``|``) to preserve exact text including
85+
newlines and quoting -- this is important for plugins like
86+
``txn_box.so`` that assign special meaning to YAML quoting.
87+
88+
.. note::
89+
90+
Structured YAML (mappings or sequences) is rejected because
91+
re-serializing through a YAML emitter strips quoting semantics that
92+
some plugins depend on. For example, ``txn_box.so`` distinguishes
93+
``"literal"`` (a quoted string) from ``extractor-name`` (an unquoted
94+
reference), and that distinction would be lost after a round-trip
95+
through ``YAML::Emitter``. Supporting structured YAML may be
96+
revisited in the future.
97+
7498
``load_order``
7599
--------------
76100

@@ -139,6 +163,8 @@ legacy :file:`plugin.config` format:
139163
plugin without removing or commenting out the line.
140164
* **Explicit load ordering** — use ``load_order`` to control loading
141165
priority independent of file position.
166+
* **Inline configuration** — embed a plugin's config content directly
167+
via the ``config`` field instead of maintaining a separate file.
142168
* **Variable expansion** — ``$record`` references in ``params`` are
143169
expanded to their current value at load time (same as
144170
:file:`plugin.config`).
@@ -207,6 +233,70 @@ Despite the YAML sequence order, the actual load order is:
207233
Use gaps between ``load_order`` values (e.g. 100, 200, 300) so new
208234
plugins can be inserted later without renumbering.
209235

236+
Inline Configuration
237+
--------------------
238+
239+
The ``config`` field lets you embed a plugin's configuration directly in
240+
:file:`plugin.yaml` instead of maintaining a separate file. At startup, |TS|
241+
writes the content to a temporary file in the configuration directory and passes
242+
the path of that file to the plugin as an argument — exactly the same way a
243+
``params`` entry pointing to an external file would work. The plugin reads the
244+
file as usual; it has no knowledge the content was inlined.
245+
246+
Use the YAML literal block scalar (``|``) to provide the content:
247+
248+
.. code-block:: yaml
249+
250+
plugins:
251+
- path: header_rewrite.so
252+
config: |
253+
cond %{SEND_RESPONSE_HDR_HOOK}
254+
set-header X-Debug "true"
255+
256+
The text after ``|`` is preserved exactly (including newlines and
257+
indentation). It is written to a temporary file named after the plugin
258+
(e.g. ``<config_dir>/.header_rewrite_inline_1.conf``). Temporary files
259+
from a previous run are removed automatically at startup.
260+
261+
This works equally well for plugins that read YAML configuration files.
262+
The block scalar preserves quoting and formatting that some YAML-consuming
263+
plugins rely on:
264+
265+
.. code-block:: yaml
266+
267+
plugins:
268+
- path: txn_box.so
269+
config: |
270+
txn_box:
271+
when: proxy-rsp
272+
do:
273+
- proxy-rsp-field<X-TxnBox>: "inline-config-active"
274+
275+
.. note::
276+
277+
The ``config`` field and ``params`` can be used together. When both are
278+
present, the temporary file path is inserted before the ``params`` entries
279+
in the argument vector:
280+
281+
.. code-block:: yaml
282+
283+
plugins:
284+
- path: header_rewrite.so
285+
config: |
286+
cond %{SEND_RESPONSE_HDR_HOOK}
287+
set-header X-Source "inline"
288+
params:
289+
- --verbose
290+
291+
The plugin receives ``argv = ["header_rewrite.so",
292+
"<config_dir>/.header_rewrite_inline_1.conf", "--verbose"]``.
293+
294+
The inline file path is always a bare positional argument at ``argv[1]``.
295+
This works for plugins that take a config file as their first argument
296+
(e.g., ``header_rewrite.so``, ``txn_box.so``). Plugins that require a
297+
flag before the filename (e.g., ``--config <file>``) should use ``params``
298+
pointing to a separate file instead of ``config``.
299+
210300
Configuration Variable Expansion
211301
---------------------------------
212302

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

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,47 @@ Display the current value of a configuration record.
732732
Display information about the registered files in |TS|. This includes the full file path, config record name, parent config (if any)
733733
if needs root access and if the file is required in |TS|.
734734

735+
.. program:: traffic_ctl config
736+
.. option:: convert <type> <args...>
737+
738+
Convert a legacy configuration file to its YAML equivalent. The conversion
739+
runs locally — no running :program:`traffic_server` is required.
740+
741+
Supported types:
742+
743+
``ssl_multicert``
744+
Convert ``ssl_multicert.config`` to :file:`ssl_multicert.yaml`.
745+
746+
.. code-block:: bash
747+
748+
traffic_ctl config convert ssl_multicert ssl_multicert.config ssl_multicert.yaml
749+
750+
``storage``
751+
Convert ``storage.config`` and ``volume.config`` to
752+
:file:`storage.yaml`.
753+
754+
.. code-block:: bash
755+
756+
traffic_ctl config convert storage storage.config volume.config storage.yaml
757+
758+
``plugin_config``
759+
Convert :file:`plugin.config` to :file:`plugin.yaml`.
760+
Commented-out lines are converted to ``enabled: false`` entries by
761+
default. Pass ``--skip-disabled`` to omit them entirely.
762+
763+
.. code-block:: bash
764+
765+
# Write to a file
766+
traffic_ctl config convert plugin_config plugin.config plugin.yaml
767+
768+
# Preview on stdout
769+
traffic_ctl config convert plugin_config plugin.config -
770+
771+
# Drop commented-out entries
772+
traffic_ctl config convert plugin_config plugin.config plugin.yaml --skip-disabled
773+
774+
For all types, use ``-`` as the output file to write to stdout.
775+
735776
.. program:: traffic_ctl config
736777
.. option:: ssl-multicert show [--yaml | --json]
737778

@@ -993,6 +1034,36 @@ traffic_ctl plugin
9931034
-------------------
9941035

9951036
.. program:: traffic_ctl plugin
1037+
.. option:: list
1038+
1039+
Display the globally loaded plugins and their status. The output includes
1040+
the configuration source, each plugin's sequence index, path,
1041+
``load_order`` (when any plugin has one), and status.
1042+
1043+
Example (with ``load_order``):
1044+
1045+
.. code-block:: bash
1046+
1047+
$ traffic_ctl plugin list
1048+
source: plugin.yaml
1049+
# plugin load_order status
1050+
1 certifier.so 100 loaded
1051+
2 header_rewrite.so -- loaded
1052+
3 debug_plugin.so -- disabled
1053+
1054+
Example (without ``load_order``):
1055+
1056+
.. code-block:: bash
1057+
1058+
$ traffic_ctl plugin list
1059+
source: plugin.yaml
1060+
# plugin status
1061+
1 stats_over_http.so loaded
1062+
2 header_rewrite.so loaded
1063+
1064+
This command requires a running :program:`traffic_server` instance — it
1065+
communicates via JSONRPC.
1066+
9961067
.. option:: msg TAG DATA
9971068

9981069
:ref:`admin_plugin_send_basic_msg`

doc/release-notes/whats-new.en.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ TS API
7676
Features
7777
--------
7878

79+
* Add :file:`plugin.yaml`, a YAML-based replacement for :file:`plugin.config`.
80+
New features include disabling plugins without deleting lines
81+
(``enabled: false``), explicit ``load_order``, inline ``config`` content, and
82+
startup logging. See :doc:`../admin-guide/files/plugin.yaml.en`.
83+
* traffic_ctl: Add ``plugin list`` to show loaded plugins and their status via
84+
JSONRPC.
85+
* traffic_ctl: Add ``config convert plugin_config`` to migrate
86+
:file:`plugin.config` to :file:`plugin.yaml`.
7987
* Add the ``cqssg`` log field for TLS group name logging
8088
* traffic_ctl: Add a new :ref:`server <traffic-control-command-server-status>` command to show some basic internal
8189
information

include/proxy/Plugin.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ struct PluginYAMLEntry {
4040
bool enabled{true};
4141
int load_order{-1};
4242
std::vector<std::string> params;
43+
std::string config_literal;
4344
};
4445

4546
using PluginYAMLEntries = std::vector<PluginYAMLEntry>;
@@ -83,6 +84,7 @@ bool plugin_yaml_init(bool validateOnly = false);
8384
bool plugin_dso_load(const char *path, void *&handle, void *&init, std::string &error);
8485

8586
/// Parse plugin.yaml and return sorted entries.
87+
/// Exposed (non-static) for unit testing.
8688
config::ConfigResult<PluginYAMLEntries> parse_plugin_yaml(const char *yaml_path);
8789

8890
/** Abstract interface class for plugin based continuations.

src/config/plugin_config.cc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
namespace
3232
{
3333

34-
constexpr char KEY_PLUGINS[] = "plugins";
34+
constexpr char KEY_PLUGINS[] = "plugins";
35+
constexpr swoc::Errata::Severity INFO_SEVERITY{0};
3536

3637
std::vector<std::string>
3738
tokenize_plugin_line(std::string_view line)
@@ -117,8 +118,10 @@ PluginConfigParser::parse_legacy(std::string_view content)
117118
ConfigResult<PluginConfigData> result;
118119
std::istringstream stream{std::string{content}};
119120
std::string line;
121+
int line_no = 0;
120122

121123
while (std::getline(stream, line)) {
124+
++line_no;
122125
std::string_view sv{line};
123126

124127
std::size_t start = 0;
@@ -151,8 +154,8 @@ PluginConfigParser::parse_legacy(std::string_view content)
151154
continue;
152155
}
153156

154-
// Heuristic: a plugin line must have a .so path as first token
155157
if (tokens[0].find(".so") == std::string::npos) {
158+
result.errata.note(INFO_SEVERITY, "skipping line {}: '{}' does not look like a plugin path (no .so)", line_no, tokens[0]);
156159
continue;
157160
}
158161

0 commit comments

Comments
 (0)