tree: 561edc288e0b00b031bfc89cdcb488765582b318
  1. unit_tests/
  2. CMakeLists.txt
  3. README.md
  4. ssl_multicert.cc
src/config/README.md

Configuration Parsing Library

This directory contains a shared configuration parsing and marshalling framework used by various ATS components including traffic_server and traffic_ctl.

Architecture Overview

Each configuration file type (ssl_multicert, etc.) follows the same pattern:

include/config/<config_name>.h   - Header with data types, parser, marshaller
src/config/<config_name>.cc      - Implementation

Components

For each configuration type, there are three main components:

  1. Entry struct - A POD (plain old data) type representing a single configuration entry (e.g., SSLMultiCertEntry).

  2. Parser class - Reads configuration files and returns parsed entries. Supports both YAML and legacy formats with automatic format detection.

  3. Marshaller class - Serializes configuration to YAML or JSON format.

Example: ssl_multicert

include/config/ssl_multicert.h
├── SSLMultiCertEntry      - Single certificate entry
├── SSLMultiCertConfig     - Vector of entries (type alias)
├── ConfigResult<T>        - Parse result with error handling
├── SSLMultiCertParser     - Parser for .yaml and .config formats
└── SSLMultiCertMarshaller - Serializer to YAML/JSON

Usage

Parsing a Configuration File

#include "config/ssl_multicert.h"

config::SSLMultiCertParser parser;
auto result = parser.parse("/path/to/ssl_multicert.yaml");

if (!result.ok()) {
    // Handle error - result.errata contains error details
    return;
}

for (const auto& entry : result.value) {
    // Use entry.ssl_cert_name, entry.dest_ip, etc.
}

Marshalling to YAML/JSON

config::SSLMultiCertMarshaller marshaller;
std::string yaml = marshaller.to_yaml(result.value);
std::string json = marshaller.to_json(result.value);

Format Detection

The parser automatically detects the configuration format:

  1. By file extension: .yaml/.yml → YAML, .config → Legacy
  2. By content inspection: Looks for ssl_multicert: (YAML) vs key=value (Legacy)

Adding a New Configuration Type

To add support for a new configuration (maybe, logging.yaml):

  1. Create include/config/logging.h:

    • Define LoggingEntry struct with fields matching the config
    • Define LoggingConfig = std::vector<LoggingEntry>
    • Declare LoggingParser and LoggingMarshaller classes
  2. Create src/config/logging.cc:

    • Implement YAML parsing using yaml-cpp
    • Implement legacy format parsing if applicable
    • Implement YAML and JSON marshalling
  3. Update src/config/CMakeLists.txt:

    • Add logging.cc to the library sources
    • Add header to CONFIG_PUBLIC_HEADERS
  4. Integrate with consumers (traffic_ctl, traffic_server, etc.)

Error Handling

All parsers return ConfigResult<T> which contains:

  • value - The parsed configuration (valid even on partial failure)
  • errata - Error/warning information using swoc::Errata
  • ok() - Returns true if no errors occurred
if (!result.ok()) {
    if (!result.errata.empty()) {
        std::cerr << result.errata.front().text() << std::endl;
    }
}

Dependencies

  • yaml-cpp - YAML parsing
  • libswoc - Error handling (swoc::Errata)

Running Unit Tests

Build with testing enabled and run:

cmake -B build -DBUILD_TESTING=ON ...
cmake --build build --target test_tsconfig
ctest --test-dir build -R test_tsconfig