/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 |