Line data Source code
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