/proc/self/cwd/source/extensions/common/tap/admin.h
Line | Count | Source (jump to first uncovered line) |
1 | | #pragma once |
2 | | |
3 | | #include <vector> |
4 | | |
5 | | #include "envoy/server/admin.h" |
6 | | #include "envoy/singleton/manager.h" |
7 | | |
8 | | #include "source/extensions/common/tap/tap.h" |
9 | | |
10 | | #include "absl/container/node_hash_set.h" |
11 | | #include "absl/types/optional.h" |
12 | | |
13 | | namespace envoy { |
14 | | namespace admin { |
15 | | namespace v3 { |
16 | | class TapRequest; |
17 | | } |
18 | | } // namespace admin |
19 | | } // namespace envoy |
20 | | |
21 | | namespace Envoy { |
22 | | namespace Extensions { |
23 | | namespace Common { |
24 | | namespace Tap { |
25 | | |
26 | | class AdminHandler; |
27 | | using AdminHandlerSharedPtr = std::shared_ptr<AdminHandler>; |
28 | | |
29 | | /** |
30 | | * Singleton /tap admin handler for admin management of tap configurations and output. This |
31 | | * handler is not installed and active unless the tap configuration specifically configures it. |
32 | | * TODO(mattklein123): We should allow the admin handler to always be installed in read only mode |
33 | | * so it's easier to debug the active tap configuration. |
34 | | */ |
35 | | class AdminHandler : public Singleton::Instance, |
36 | | public Extensions::Common::Tap::Sink, |
37 | | Logger::Loggable<Logger::Id::tap> { |
38 | | public: |
39 | | AdminHandler(OptRef<Server::Admin> admin, Event::Dispatcher& main_thread_dispatcher); |
40 | | ~AdminHandler() override; |
41 | | |
42 | | /** |
43 | | * Get the singleton admin handler. The handler will be created if it doesn't already exist, |
44 | | * otherwise the existing handler will be returned. |
45 | | */ |
46 | | static AdminHandlerSharedPtr getSingleton(OptRef<Server::Admin> admin, |
47 | | Singleton::Manager& singleton_manager, |
48 | | Event::Dispatcher& main_thread_dispatcher); |
49 | | |
50 | | /** |
51 | | * Register a new extension config to the handler so that it can be admin managed. |
52 | | * @param config supplies the config to register. |
53 | | * @param config_id supplies the ID to use for managing the configuration. Multiple extensions |
54 | | * can use the same ID so they can be managed in aggregate (e.g., an HTTP filter on |
55 | | * many listeners). |
56 | | */ |
57 | | void registerConfig(ExtensionConfig& config, const std::string& config_id); |
58 | | |
59 | | /** |
60 | | * Unregister an extension config from the handler. |
61 | | * @param config supplies the previously registered config. |
62 | | */ |
63 | | void unregisterConfig(ExtensionConfig& config); |
64 | | |
65 | | // Extensions::Common::Tap::Sink |
66 | | PerTapSinkHandlePtr |
67 | | createPerTapSinkHandle(uint64_t trace_id, |
68 | | envoy::config::tap::v3::OutputSink::OutputSinkTypeCase type) override; |
69 | | |
70 | | private: |
71 | | /** |
72 | | * TraceBuffer internally buffers TraceWrappers in a vector. The size of the |
73 | | * internal buffer is determined on construction by max_buf_size. TraceBuffer will continue to |
74 | | * buffer traces via calls to bufferTrace until there is no remaining room in the buffer, or |
75 | | * traceList is called, transfering ownership of the buffered data. |
76 | | * Note that TraceBuffer is not threadsafe by itself - accesses to TraceBuffer need to be |
77 | | * serialized. Serialization is currently done by the main thread dispatcher. |
78 | | */ |
79 | | class TraceBuffer { |
80 | | using TraceWrapper = envoy::data::tap::v3::TraceWrapper; |
81 | | |
82 | | public: |
83 | | TraceBuffer(uint64_t max_buf_size) |
84 | 0 | : max_buf_size_(max_buf_size), buffer_(std::vector<TraceWrapper>()) { |
85 | 0 | buffer_->reserve(max_buf_size); |
86 | 0 | } |
87 | | |
88 | | // Buffers trace internally if there is space available |
89 | | // This function takes exclusive ownership of trace and may destroy the content of trace. |
90 | | // A unique_ptr is semantically more correct here, but a shared pointer is |
91 | | // needed for traces to be captured in lambda function (submitTrace). |
92 | | void bufferTrace(const std::shared_ptr<envoy::data::tap::v3::TraceWrapper>& trace); |
93 | | |
94 | | // Returns true if the trace buffer is full (reached max_buf_size_) false otherwise |
95 | 0 | bool full() const { |
96 | 0 | if (!buffer_) { |
97 | 0 | return false; |
98 | 0 | } |
99 | 0 | return (buffer_->size() == max_buf_size_); |
100 | 0 | } |
101 | | |
102 | | // Return true if the buffer has already been flushed, false otherwise. |
103 | 0 | bool flushed() const { return !buffer_; } |
104 | | |
105 | | // Take ownership of the internally managed trace list |
106 | 0 | std::vector<TraceWrapper> flush() { |
107 | 0 | std::vector<TraceWrapper> buffer = std::move(*buffer_); |
108 | 0 | buffer_.reset(); // set optional to empty |
109 | 0 | return buffer; |
110 | 0 | } |
111 | | |
112 | | private: |
113 | | const size_t max_buf_size_; // Number of traces to buffer |
114 | | absl::optional<std::vector<TraceWrapper>> buffer_; |
115 | | }; |
116 | | |
117 | | /** |
118 | | * This object's lifetime is tied to the lifetime of the admin_stream, and is responsible for |
119 | | * managing all data that has lifetime tied to the admin_stream. |
120 | | */ |
121 | | class AttachedRequest { |
122 | | public: |
123 | | AttachedRequest(AdminHandler* admin_handler, const envoy::admin::v3::TapRequest& tap_request, |
124 | | Server::AdminStream* admin_stream); |
125 | 0 | virtual ~AttachedRequest() = default; |
126 | | // Stream the protobuf message to the admin_stream using the configured format |
127 | | // Requires the admin_stream to be open |
128 | | virtual void streamMsg(const Protobuf::Message& message, bool end_stream = false); |
129 | | |
130 | | // Explicitly close the admin_stream. Stream must be open. |
131 | | virtual void endStream(); |
132 | | |
133 | | // Factory method for AttachedRequests - uses protobuf input to determine the subtype of |
134 | | // AttachedRequest to create |
135 | | static std::shared_ptr<AttachedRequest> |
136 | | createAttachedRequest(AdminHandler* admin_handler, |
137 | | const envoy::admin::v3::TapRequest& tap_request, |
138 | | Server::AdminStream* admin_stream); |
139 | | |
140 | | // --------- Accessors --------- |
141 | | // Get a pointer to the internal trace buffer. This method only applies for |
142 | | // the Buffered sink type, but exists in the generic API to avoid |
143 | | // dynamic casting of the AttachedRequest type elsewhere |
144 | 0 | virtual TraceBuffer* traceBuffer() const { return nullptr; } |
145 | 0 | const std::string& id() const { return config_id_; } |
146 | 0 | const envoy::config::tap::v3::TapConfig& config() const { return config_; } |
147 | 0 | envoy::config::tap::v3::OutputSink::Format format() const { |
148 | 0 | return config_.output_config().sinks()[0].format(); |
149 | 0 | } |
150 | | |
151 | | protected: |
152 | 0 | Event::Dispatcher& dispatcher() { return main_thread_dispatcher_; } |
153 | 0 | const Server::AdminStream* stream() const { return admin_stream_; } |
154 | | |
155 | | private: |
156 | | const std::string config_id_; |
157 | | const envoy::config::tap::v3::TapConfig config_; |
158 | | const Server::AdminStream* admin_stream_; |
159 | | Event::Dispatcher& main_thread_dispatcher_; |
160 | | friend class BaseAdminHandlerTest; |
161 | | }; |
162 | | |
163 | | /** |
164 | | * AttachedRequest with additional data specific to the Buffered Sink type |
165 | | */ |
166 | | class AttachedRequestBuffered : public AttachedRequest { |
167 | | // Callback fired on timer expiry |
168 | | void onTimeout(const std::weak_ptr<AttachedRequest>& attached_request); |
169 | | |
170 | | public: |
171 | 0 | TraceBuffer* traceBuffer() const override { return trace_buffer_.get(); } |
172 | | |
173 | | AttachedRequestBuffered(AdminHandler* admin_handler, |
174 | | const envoy::admin::v3::TapRequest& tap_request, |
175 | | Server::AdminStream* admin_stream); |
176 | | |
177 | | private: |
178 | | Event::TimerPtr timer_; |
179 | | // Pointer to buffered traces, only exists if the sink type requires buffering multiple traces |
180 | | std::unique_ptr<TraceBuffer> trace_buffer_; |
181 | | friend class BufferedAdminHandlerTest; // For testing Purposes |
182 | | }; |
183 | | |
184 | | struct AdminPerTapSinkHandle : public PerTapSinkHandle { |
185 | 0 | AdminPerTapSinkHandle(AdminHandler& parent) : parent_(parent) {} |
186 | | |
187 | | // Extensions::Common::Tap::PerTapSinkHandle |
188 | | void submitTrace(TraceWrapperPtr&& trace, |
189 | | envoy::config::tap::v3::OutputSink::Format format) override; |
190 | | |
191 | | AdminHandler& parent_; |
192 | | }; |
193 | | |
194 | | /** |
195 | | * Sink for buffering a variable number of traces in a TraceBuffer |
196 | | */ |
197 | | struct BufferedPerTapSinkHandle : public PerTapSinkHandle { |
198 | 0 | BufferedPerTapSinkHandle(AdminHandler& parent) : parent_(parent) {} |
199 | | |
200 | | // Extensions::Common::Tap::PerTapSinkHandle |
201 | | void submitTrace(TraceWrapperPtr&& trace, |
202 | | envoy::config::tap::v3::OutputSink::Format format) override; |
203 | | |
204 | | AdminHandler& parent_; |
205 | | }; |
206 | | |
207 | | Http::Code handler(Http::HeaderMap& response_headers, Buffer::Instance& response, |
208 | | Server::AdminStream& admin_stream); |
209 | | Http::Code badRequest(Buffer::Instance& response, absl::string_view error); |
210 | | |
211 | | Server::Admin& admin_; |
212 | | Event::Dispatcher& main_thread_dispatcher_; |
213 | | absl::node_hash_map<std::string, absl::node_hash_set<ExtensionConfig*>> config_id_map_; |
214 | | std::shared_ptr<AttachedRequest> attached_request_; |
215 | | friend class BaseAdminHandlerTest; // For testing purposes |
216 | | friend class BufferedAdminHandlerTest; // For testing Purposes |
217 | | }; |
218 | | |
219 | | } // namespace Tap |
220 | | } // namespace Common |
221 | | } // namespace Extensions |
222 | | } // namespace Envoy |