Coverage Report

Created: 2024-09-19 09:45

/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