Coverage Report

Created: 2026-06-27 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/trafficserver/include/mgmt/config/ConfigContext.h
Line
Count
Source
1
/** @file
2
3
   ConfigContext - Context for configuration loading/reloading operations
4
5
   Provides:
6
   - Status tracking (in_progress, complete, fail, log)
7
   - Inline content support for YAML configs (via -d flag or RPC API)
8
9
  @section license License
10
11
  Licensed to the Apache Software Foundation (ASF) under one
12
  or more contributor license agreements.  See the NOTICE file
13
  distributed with this work for additional information
14
  regarding copyright ownership.  The ASF licenses this file
15
  to you under the Apache License, Version 2.0 (the
16
  "License"); you may not use this file except in compliance
17
  with the License.  You may obtain a copy of the License at
18
19
      http://www.apache.org/licenses/LICENSE-2.0
20
21
  Unless required by applicable law or agreed to in writing, software
22
  distributed under the License is distributed on an "AS IS" BASIS,
23
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
  See the License for the specific language governing permissions and
25
  limitations under the License.
26
 */
27
28
#pragma once
29
30
#include <memory>
31
#include <string>
32
#include <string_view>
33
34
#include "swoc/Errata.h"
35
#include "swoc/BufferWriter.h"
36
#include "tsutil/ts_diag_levels.h"
37
#include "yaml-cpp/node/node.h"
38
39
// Forward declarations
40
class ConfigReloadTask;
41
class ReloadCoordinator;
42
namespace config
43
{
44
class ConfigRegistry;
45
}
46
47
///
48
/// @brief Context passed to config handlers during load/reload operations.
49
///
50
/// This object is passed to reconfigure() methods to:
51
/// 1. Track progress/status of the operation (in_progress, complete, fail, log)
52
/// 2. Provide RPC-supplied YAML content (for -d flag (traffic_ctl) or direct JSONRPC calls)
53
///
54
/// For file-based reloads, handlers read from their own registered filename.
55
/// For RPC reloads, handlers use supplied_yaml() to get the content.
56
///
57
/// @note This context is also used during **startup** configuration loading.
58
///       At startup there is no active reload task, so all status operations
59
///       (in_progress, complete, fail, log) are safe **no-ops**. To keep the
60
///       existing code logic for loading/reloading this design aims to avoid
61
///       having two separate code paths for startup vs. reload — handlers
62
///       can use the same API in both cases.
63
///
64
/// Usage:
65
/// @code
66
///   void MyConfig::reconfigure(ConfigContext ctx) {
67
///     ctx.in_progress();
68
///
69
///     YAML::Node root;
70
///     if (auto yaml = ctx.supplied_yaml()) {
71
///       // RPC mode: content provided via -d flag or RPC.
72
///       // YAML::Node has explicit operator bool() → true when IsDefined().
73
///       // Copy is cheap (internally reference-counted).
74
///       root = yaml;
75
///     } else {
76
///       // File mode: read from registered filename.
77
///       root = YAML::LoadFile(my_config_filename);
78
///     }
79
///
80
///     // ... process config ...
81
///
82
///     ctx.complete("Loaded successfully");
83
///   }
84
/// @endcode
85
///
86
class ConfigContext
87
{
88
public:
89
  ConfigContext();
90
91
  explicit ConfigContext(std::shared_ptr<ConfigReloadTask> t, std::string_view description = "", std::string_view filename = "");
92
93
  ~ConfigContext();
94
95
  // Copy only — move is intentionally suppressed.
96
  // ConfigContext holds a weak_ptr (cheap to copy) and a YAML::Node (ref-counted).
97
  // Suppressing move ensures that std::move(ctx) silently copies, keeping the
98
  // original valid. This is critical for execute_reload()'s post-handler check:
99
  // if a handler defers work (e.g. LogConfig), the original ctx must remain
100
  // valid so is_terminal() can detect the non-terminal state and emit a warning.
101
  ConfigContext(ConfigContext const &);
102
  ConfigContext &operator=(ConfigContext const &);
103
104
  /// Check if this context wraps a live task.
105
  /// Returns false for default-constructed or dedup-rejected contexts.
106
  explicit
107
  operator bool() const
108
0
  {
109
0
    return !_task.expired();
110
0
  }
111
112
  void in_progress(std::string_view text = "");
113
  template <typename... Args>
114
  void
115
  in_progress(swoc::TextView fmt, Args &&...args)
116
  {
117
    std::string buf;
118
    in_progress(swoc::bwprint(buf, fmt, std::forward<Args>(args)...));
119
  }
120
121
  void log(std::string_view text);
122
  void log(DiagsLevel level, std::string_view text);
123
  void log(swoc::Errata const &errata);
124
  template <typename... Args>
125
  void
126
  log(swoc::TextView fmt, Args &&...args)
127
  {
128
    std::string buf;
129
    log(swoc::bwprint(buf, fmt, std::forward<Args>(args)...));
130
  }
131
132
  /// Mark operation as successfully completed
133
  void complete(std::string_view text = "");
134
  template <typename... Args>
135
  void
136
  complete(swoc::TextView fmt, Args &&...args)
137
  {
138
    std::string buf;
139
    complete(swoc::bwprint(buf, fmt, std::forward<Args>(args)...));
140
  }
141
142
  /// Mark operation as failed.
143
  void fail(swoc::Errata const &errata, std::string_view summary = "");
144
  void fail(std::string_view reason = "");
145
  template <typename... Args>
146
  void
147
  fail(swoc::TextView fmt, Args &&...args)
148
  {
149
    std::string buf;
150
    fail(swoc::bwprint(buf, fmt, std::forward<Args>(args)...));
151
  }
152
  /// Eg: fail(errata, "Failed to load config: {}", filename);
153
  template <typename... Args>
154
  void
155
  fail(swoc::Errata const &errata, swoc::TextView fmt, Args &&...args)
156
  {
157
    std::string buf;
158
    fail(errata, swoc::bwprint(buf, fmt, std::forward<Args>(args)...));
159
  }
160
161
  /// Check if the task has reached a terminal state (SUCCESS, FAIL, TIMEOUT).
162
  [[nodiscard]] bool is_terminal() const;
163
164
  /// Get the description associated with this context's task.
165
  /// For registered configs this is the registration key (e.g., "sni", "ssl").
166
  /// For dependent contexts it is the label passed to add_dependent_ctx().
167
  [[nodiscard]] std::string get_description() const;
168
169
  /// Create a dependent sub-task that tracks progress independently under this parent.
170
  /// Each dependent reports its own status (in_progress/complete/fail) and the parent
171
  /// task aggregates them. The dependent context also inherits the parent's supplied YAML node.
172
  ///
173
  [[nodiscard]] ConfigContext add_dependent_ctx(std::string_view description = "", std::string_view filename = "");
174
175
  /// Get supplied YAML node (for RPC-based reloads).
176
  /// Returns Undefined when no content was provided (operator bool() == false).
177
  /// @code
178
  ///   if (auto yaml = ctx.supplied_yaml()) { /* use yaml node */ }
179
  /// @endcode
180
  /// @return copy of the supplied YAML node (cheap — YAML::Node is internally reference-counted).
181
  [[nodiscard]] YAML::Node supplied_yaml() const;
182
183
  /// Get reload directives extracted from the _reload key.
184
  /// Directives are operational parameters that modify how the handler performs
185
  /// the reload (e.g. scope to a single entry, dry-run) — distinct from config content.
186
  /// The framework extracts _reload from the supplied node before passing content
187
  /// to the handler, so supplied_yaml() never contains _reload.
188
  /// Returns Undefined when no directives were provided (operator bool() == false).
189
  /// @code
190
  ///   if (auto directives = ctx.reload_directives()) { /* use directives */ }
191
  /// @endcode
192
  /// @return copy of the directives YAML node (cheap — YAML::Node is internally reference-counted).
193
  [[nodiscard]] YAML::Node reload_directives() const;
194
195
private:
196
  /// Set supplied YAML node. Only ConfigRegistry should call this during reload setup.
197
  void set_supplied_yaml(YAML::Node node);
198
199
  /// Set reload directives. Only ConfigRegistry should call this during reload setup.
200
  void set_reload_directives(YAML::Node node);
201
202
  std::weak_ptr<ConfigReloadTask> _task;
203
  YAML::Node                      _supplied_yaml{YAML::NodeType::Undefined};
204
  YAML::Node                      _reload_directives{YAML::NodeType::Undefined};
205
206
  friend class ReloadCoordinator;
207
  friend class config::ConfigRegistry;
208
};
209
210
namespace config
211
{
212
/// Create a ConfigContext for use in reconfigure handlers
213
ConfigContext make_config_reload_context(std::string_view description, std::string_view filename = "");
214
} // namespace config