Skip to content

Commit 3ad74ce

Browse files
committed
to_json: use yamlcpp; support yaml output
This also makes yaml the default traffic_ctl config ssl_multicert show format
1 parent 72e817d commit 3ad74ce

File tree

12 files changed

+193
-91
lines changed

12 files changed

+193
-91
lines changed

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

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

314+
.. program:: traffic_ctl config
315+
.. option:: ssl-multicert show [--yaml | --json]
316+
317+
Display the current ``ssl_multicert.yaml`` configuration. By default, output is in YAML format.
318+
Use ``--json`` or ``-j`` to output in JSON format.
319+
320+
.. option:: --yaml, -y
321+
322+
Output in YAML format (default).
323+
324+
.. option:: --json, -j
325+
326+
Output in JSON format.
327+
328+
Example:
329+
330+
.. code-block:: bash
331+
332+
$ traffic_ctl config ssl-multicert show
333+
ssl_multicert:
334+
- ssl_cert_name: server.pem
335+
dest_ip: "*"
336+
ssl_key_name: server.key
337+
338+
.. code-block:: bash
339+
340+
$ traffic_ctl config ssl-multicert show --json
341+
{"ssl_multicert": [{"ssl_cert_name": "server.pem", "dest_ip": "*", "ssl_key_name": "server.key"}]}
342+
314343
.. _traffic-control-command-metric:
315344

316345
traffic_ctl metric

src/config/ssl_multicert.cc

Lines changed: 12 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -129,44 +129,6 @@ parse_legacy_line(std::string_view line)
129129
return result;
130130
}
131131

132-
/// Escape a string for JSON output.
133-
std::string
134-
json_escape(std::string const &s)
135-
{
136-
std::string result;
137-
result.reserve(s.size() + 2);
138-
result += '"';
139-
for (char c : s) {
140-
switch (c) {
141-
case '"':
142-
result += "\\\"";
143-
break;
144-
case '\\':
145-
result += "\\\\";
146-
break;
147-
case '\b':
148-
result += "\\b";
149-
break;
150-
case '\f':
151-
result += "\\f";
152-
break;
153-
case '\n':
154-
result += "\\n";
155-
break;
156-
case '\r':
157-
result += "\\r";
158-
break;
159-
case '\t':
160-
result += "\\t";
161-
break;
162-
default:
163-
result += c;
164-
}
165-
}
166-
result += '"';
167-
return result;
168-
}
169-
170132
/// Escape a string for YAML output if needed.
171133
std::string
172134
yaml_escape(std::string const &value)
@@ -456,39 +418,24 @@ SSLMultiCertMarshaller::to_yaml(SSLMultiCertConfig const &config)
456418
std::string
457419
SSLMultiCertMarshaller::to_json(SSLMultiCertConfig const &config)
458420
{
459-
std::ostringstream out;
460-
out << "{\n \"ssl_multicert\": [\n";
421+
YAML::Emitter json;
422+
json << YAML::DoubleQuoted << YAML::Flow;
423+
json << YAML::BeginMap;
424+
json << YAML::Key << KEY_SSL_MULTICERT << YAML::Value << YAML::BeginSeq;
461425

462-
bool first_entry = true;
463426
for (auto const &entry : config) {
464-
if (!first_entry) {
465-
out << ",\n";
466-
}
467-
first_entry = false;
468-
469-
out << " {";
470-
bool first_field = true;
427+
json << YAML::BeginMap;
471428

472429
auto write_field = [&](char const *key, std::string const &value) {
473-
if (value.empty()) {
474-
return;
475-
}
476-
if (!first_field) {
477-
out << ", ";
430+
if (!value.empty()) {
431+
json << YAML::Key << key << YAML::Value << value;
478432
}
479-
first_field = false;
480-
out << json_escape(key) << ": " << json_escape(value);
481433
};
482434

483435
auto write_int_field = [&](char const *key, std::optional<int> const &value) {
484-
if (!value.has_value()) {
485-
return;
436+
if (value.has_value()) {
437+
json << YAML::Key << key << YAML::Value << value.value();
486438
}
487-
if (!first_field) {
488-
out << ", ";
489-
}
490-
first_field = false;
491-
out << json_escape(key) << ": " << value.value();
492439
};
493440

494441
write_field(KEY_SSL_CERT_NAME, entry.ssl_cert_name);
@@ -502,11 +449,11 @@ SSLMultiCertMarshaller::to_json(SSLMultiCertConfig const &config)
502449
write_int_field(KEY_SSL_TICKET_ENABLED, entry.ssl_ticket_enabled);
503450
write_int_field(KEY_SSL_TICKET_NUMBER, entry.ssl_ticket_number);
504451

505-
out << "}";
452+
json << YAML::EndMap;
506453
}
507454

508-
out << "\n ]\n}\n";
509-
return out.str();
455+
json << YAML::EndSeq << YAML::EndMap;
456+
return json.c_str();
510457
}
511458

512459
} // namespace config

src/traffic_ctl/SSLMultiCertCommand.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ SSLMultiCertCommand::SSLMultiCertCommand(ts::Arguments *args) : CtrlCommand(args
5252
_printer = std::make_unique<GenericPrinter>(print_opts);
5353

5454
if (args->get("show")) {
55+
// Default to YAML; use JSON only if explicitly requested.
56+
_output_json = args->get("json");
5557
_invoked_func = [this]() { show_config(); };
5658
} else {
5759
throw std::invalid_argument("Unsupported ssl-multicert subcommand");
@@ -77,8 +79,6 @@ SSLMultiCertCommand::show_config()
7779
}
7880

7981
config::SSLMultiCertMarshaller marshaller;
80-
81-
// Output in JSON format for easy consumption by tools.
82-
std::string const output = marshaller.to_json(result.value);
82+
std::string const output = _output_json ? marshaller.to_json(result.value) : marshaller.to_yaml(result.value);
8383
_printer->write_output(output);
8484
}

src/traffic_ctl/SSLMultiCertCommand.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,6 @@ class SSLMultiCertCommand : public CtrlCommand
4242

4343
private:
4444
void show_config();
45+
46+
bool _output_json = false;
4547
};

src/traffic_ctl/traffic_ctl.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,13 @@ main([[maybe_unused]] int argc, const char **argv)
143143
// ssl-multicert subcommand
144144
auto &ssl_multicert_command =
145145
config_command.add_command("ssl-multicert", "Manage ssl_multicert configuration").require_commands();
146-
ssl_multicert_command.add_command("show", "Show the ssl_multicert configuration in JSON format", Command_Execute)
147-
.add_example_usage("traffic_ctl config ssl-multicert show");
146+
auto &ssl_multicert_show = ssl_multicert_command.add_command("show", "Show the ssl_multicert configuration", Command_Execute)
147+
.add_example_usage("traffic_ctl config ssl-multicert show")
148+
.add_example_usage("traffic_ctl config ssl-multicert show --yaml")
149+
.add_example_usage("traffic_ctl config ssl-multicert show --json");
150+
ssl_multicert_show.add_mutex_group("format", false, "Output format");
151+
ssl_multicert_show.add_option_to_group("format", "--yaml", "-y", "Output in YAML format (default)");
152+
ssl_multicert_show.add_option_to_group("format", "--json", "-j", "Output in JSON format");
148153

149154
// convert subcommand - convert config files between formats
150155
auto &convert_command = config_command.add_command("convert", "Convert configuration files to YAML format").require_commands();

tests/gold_tests/tls/ssl_key_dialog.test.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,13 @@
4040
})
4141

4242
ts.Disk.ssl_multicert_yaml.AddLines(
43-
[
44-
'dest_ip=* ssl_cert_name=passphrase.pem ssl_key_name=passphrase.key ssl_key_dialog="exec:/bin/bash -c \'echo -n passphrase\'"',
45-
])
43+
"""
44+
ssl_multicert:
45+
- dest_ip: "*"
46+
ssl_cert_name: passphrase.pem
47+
ssl_key_name: passphrase.key
48+
ssl_key_dialog: "exec:/bin/bash -c 'echo -n passphrase'"
49+
""".split("\n"))
4650

4751
request_header = {"headers": "GET / HTTP/1.1\r\nHost: bogus\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
4852
response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": "success!"}
@@ -65,10 +69,14 @@
6569
# Update the multicert config
6670
sslcertpath = ts.Disk.ssl_multicert_yaml.AbsPath
6771
tr2.Disk.File(sslcertpath, id="ssl_multicert_config", typename="ats:config"),
68-
tr2.Disk.ssl_multicert_yaml.AddLines(
69-
[
70-
'dest_ip=* ssl_cert_name=passphrase2.pem ssl_key_name=passphrase2.key ssl_key_dialog="exec:/bin/bash -c \'echo -n passphrase\'"',
71-
])
72+
tr2.Disk.ssl_multicert_config.AddLines(
73+
"""
74+
ssl_multicert:
75+
- dest_ip: "*"
76+
ssl_cert_name: passphrase2.pem
77+
ssl_key_name: passphrase2.key
78+
ssl_key_dialog: "exec:/bin/bash -c 'echo -n passphrase'"
79+
""".split("\n"))
7280
tr2.StillRunningAfter = ts
7381
tr2.StillRunningAfter = server
7482
tr2.Processes.Default.Command = 'echo Updated configs'

tests/gold_tests/tls/tls_check_cert_select_plugin.test.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,17 @@
4545
ts.Disk.remap_config.AddLine('map / https://foo.com:{1}'.format(ts.Variables.ssl_port, server.Variables.SSL_Port))
4646

4747
ts.Disk.ssl_multicert_yaml.AddLines(
48-
[
49-
'dest_ip=127.0.0.1 ssl_cert_name=signed-foo.pem ssl_key_name=signed-foo.key',
50-
'ssl_cert_name=signed2-bar.pem ssl_key_name=signed-bar.key',
51-
'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key',
52-
])
48+
"""
49+
ssl_multicert:
50+
- dest_ip: 127.0.0.1
51+
ssl_cert_name: signed-foo.pem
52+
ssl_key_name: signed-foo.key
53+
- ssl_cert_name: signed2-bar.pem
54+
ssl_key_name: signed-bar.key
55+
- dest_ip: "*"
56+
ssl_cert_name: server.pem
57+
ssl_key_name: server.key
58+
""".split("\n"))
5359

5460
Test.PrepareTestPlugin(os.path.join(Test.Variables.AtsTestPluginsDir, 'ssl_secret_load_test.so'), ts)
5561

tests/gold_tests/tls/tls_check_cert_selection_reload.test.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,13 @@
4040
ts.Disk.remap_config.AddLine('map /stuff https://foo.com:{1}'.format(ts.Variables.ssl_port, server.Variables.SSL_Port))
4141

4242
ts.Disk.ssl_multicert_yaml.AddLines(
43-
[
44-
'ssl_cert_name=signed-bar.pem ssl_key_name=signed-bar.key',
45-
'dest_ip=* ssl_cert_name=combo.pem',
46-
])
43+
"""
44+
ssl_multicert:
45+
- ssl_cert_name: signed-bar.pem
46+
ssl_key_name: signed-bar.key
47+
- dest_ip: "*"
48+
ssl_cert_name: combo.pem
49+
""".split("\n"))
4750

4851
# Case 1, global config policy=permissive properties=signature
4952
# override for foo.com policy=enforced properties=all

tests/gold_tests/tls/tls_check_dual_cert_selection.test.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,16 @@
4949
ts.Disk.remap_config.AddLine('map / https://foo.com:{1}'.format(ts.Variables.ssl_port, server.Variables.SSL_Port))
5050

5151
ts.Disk.ssl_multicert_yaml.AddLines(
52-
[
53-
'ssl_cert_name=signed-foo-ec.pem,signed-foo.pem ssl_key_name=signed-foo-ec.key,signed-foo.key',
54-
'ssl_cert_name=signed-san-ec.pem,signed-san.pem ssl_key_name=signed-san-ec.key,signed-san.key',
55-
'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key',
56-
])
52+
"""
53+
ssl_multicert:
54+
- ssl_cert_name: signed-foo-ec.pem,signed-foo.pem
55+
ssl_key_name: signed-foo-ec.key,signed-foo.key
56+
- ssl_cert_name: signed-san-ec.pem,signed-san.pem
57+
ssl_key_name: signed-san-ec.key,signed-san.key
58+
- dest_ip: "*"
59+
ssl_cert_name: server.pem
60+
ssl_key_name: server.key
61+
""".split("\n"))
5762

5863
# Case 1, global config policy=permissive properties=signature
5964
# override for foo.com policy=enforced properties=all
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"ssl_multicert": [{"ssl_cert_name": "server.pem", "dest_ip": "*", "ssl_key_name": "server.key"}]}

0 commit comments

Comments
 (0)