1
#pragma once
2

            
3
#include <string>
4

            
5
#include "envoy/common/optref.h"
6
#include "envoy/event/dispatcher.h"
7
#include "envoy/http/async_client.h"
8
#include "envoy/server/factory_context.h"
9
#include "envoy/server/listener_manager.h"
10
#include "envoy/stats/scope.h"
11
#include "envoy/stats/stats.h"
12
#include "envoy/stats/store.h"
13
#include "envoy/upstream/cluster_manager.h"
14

            
15
#include "source/common/common/logger.h"
16
#include "source/common/http/message_impl.h"
17
#include "source/common/init/target_impl.h"
18
#include "source/common/stats/utility.h"
19
#include "source/extensions/dynamic_modules/abi/abi.h"
20
#include "source/extensions/dynamic_modules/dynamic_modules.h"
21

            
22
#include "absl/container/flat_hash_map.h"
23

            
24
namespace Envoy {
25
namespace Extensions {
26
namespace Bootstrap {
27
namespace DynamicModules {
28

            
29
// The default custom stat namespace which prepends all user-defined metrics.
30
// This can be overridden via the ``metrics_namespace`` field in ``DynamicModuleConfig``.
31
constexpr absl::string_view DefaultMetricsNamespace = "dynamicmodulescustom";
32

            
33
using OnBootstrapExtensionConfigDestroyType =
34
    decltype(&envoy_dynamic_module_on_bootstrap_extension_config_destroy);
35
using OnBootstrapExtensionNewType = decltype(&envoy_dynamic_module_on_bootstrap_extension_new);
36
using OnBootstrapExtensionServerInitializedType =
37
    decltype(&envoy_dynamic_module_on_bootstrap_extension_server_initialized);
38
using OnBootstrapExtensionWorkerThreadInitializedType =
39
    decltype(&envoy_dynamic_module_on_bootstrap_extension_worker_thread_initialized);
40
using OnBootstrapExtensionDestroyType =
41
    decltype(&envoy_dynamic_module_on_bootstrap_extension_destroy);
42
using OnBootstrapExtensionDrainStartedType =
43
    decltype(&envoy_dynamic_module_on_bootstrap_extension_drain_started);
44
using OnBootstrapExtensionShutdownType =
45
    decltype(&envoy_dynamic_module_on_bootstrap_extension_shutdown);
46
using OnBootstrapExtensionConfigScheduledType =
47
    decltype(&envoy_dynamic_module_on_bootstrap_extension_config_scheduled);
48
using OnBootstrapExtensionHttpCalloutDoneType =
49
    decltype(&envoy_dynamic_module_on_bootstrap_extension_http_callout_done);
50
using OnBootstrapExtensionTimerFiredType =
51
    decltype(&envoy_dynamic_module_on_bootstrap_extension_timer_fired);
52
using OnBootstrapExtensionAdminRequestType =
53
    decltype(&envoy_dynamic_module_on_bootstrap_extension_admin_request);
54
using OnBootstrapExtensionClusterAddOrUpdateType =
55
    decltype(&envoy_dynamic_module_on_bootstrap_extension_cluster_add_or_update);
56
using OnBootstrapExtensionClusterRemovalType =
57
    decltype(&envoy_dynamic_module_on_bootstrap_extension_cluster_removal);
58
using OnBootstrapExtensionListenerAddOrUpdateType =
59
    decltype(&envoy_dynamic_module_on_bootstrap_extension_listener_add_or_update);
60
using OnBootstrapExtensionListenerRemovalType =
61
    decltype(&envoy_dynamic_module_on_bootstrap_extension_listener_removal);
62

            
63
class DynamicModuleBootstrapExtension;
64

            
65
/**
66
 * A config to create bootstrap extensions based on a dynamic module. This will be owned by the
67
 * bootstrap extension. This resolves and holds the symbols used for the bootstrap extension.
68
 */
69
class DynamicModuleBootstrapExtensionConfig
70
    : public std::enable_shared_from_this<DynamicModuleBootstrapExtensionConfig>,
71
      public Upstream::ClusterUpdateCallbacks,
72
      public Server::ListenerUpdateCallbacks,
73
      public Logger::Loggable<Logger::Id::dynamic_modules> {
74
public:
75
  /**
76
   * Constructor for the config.
77
   * @param extension_name the name of the extension.
78
   * @param extension_config the configuration for the module.
79
   * @param metrics_namespace the namespace prefix for metrics emitted by this module.
80
   * @param dynamic_module the dynamic module to use.
81
   * @param main_thread_dispatcher the main thread dispatcher.
82
   * @param context the server factory context for accessing cluster manager lazily.
83
   * @param stats_store the stats store for accessing metrics.
84
   */
85
  DynamicModuleBootstrapExtensionConfig(const absl::string_view extension_name,
86
                                        const absl::string_view extension_config,
87
                                        const absl::string_view metrics_namespace,
88
                                        Extensions::DynamicModules::DynamicModulePtr dynamic_module,
89
                                        Event::Dispatcher& main_thread_dispatcher,
90
                                        Server::Configuration::ServerFactoryContext& context,
91
                                        Stats::Store& stats_store);
92

            
93
  ~DynamicModuleBootstrapExtensionConfig();
94

            
95
  /**
96
   * This is called when an event is scheduled via
97
   * DynamicModuleBootstrapExtensionConfigScheduler::commit.
98
   */
99
  void onScheduled(uint64_t event_id);
100

            
101
  /**
102
   * Helper to get the `this` pointer as a void pointer.
103
   */
104
94
  void* thisAsVoidPtr() { return static_cast<void*>(this); }
105

            
106
  /**
107
   * Sends an HTTP callout to the specified cluster with the given message.
108
   * This must be called on the main thread.
109
   *
110
   * @param callout_id_out is a pointer to a variable where the callout ID will be stored.
111
   * @param cluster_name is the name of the cluster to which the callout is sent.
112
   * @param message is the HTTP request message to send.
113
   * @param timeout_milliseconds is the timeout for the callout in milliseconds.
114
   * @return the result of the callout initialization.
115
   */
116
  envoy_dynamic_module_type_http_callout_init_result
117
  sendHttpCallout(uint64_t* callout_id_out, absl::string_view cluster_name,
118
                  Http::RequestMessagePtr&& message, uint64_t timeout_milliseconds);
119

            
120
  /**
121
   * Signals that the module's initialization is complete. This unblocks the init manager and
122
   * allows Envoy to start accepting traffic. An init target is automatically registered for every
123
   * bootstrap extension, so the module must call this exactly once to unblock startup.
124
   */
125
  void signalInitComplete();
126

            
127
  /**
128
   * Enables cluster lifecycle event notifications. When enabled, the module will receive
129
   * on_bootstrap_extension_cluster_add_or_update and on_bootstrap_extension_cluster_removal
130
   * callbacks when clusters are added, updated, or removed.
131
   *
132
   * This must be called on the main thread after the server is initialized, since the
133
   * ClusterManager is not available during bootstrap extension creation.
134
   *
135
   * @return true if the callbacks were successfully registered, false if already registered.
136
   */
137
  bool enableClusterLifecycle();
138

            
139
  // Upstream::ClusterUpdateCallbacks
140
  void onClusterAddOrUpdate(absl::string_view cluster_name,
141
                            Upstream::ThreadLocalClusterCommand& get_cluster) override;
142
  void onClusterRemoval(const std::string& cluster_name) override;
143

            
144
  /**
145
   * Sets the listener manager reference. Must be called during onServerInitialized before
146
   * the module can enable listener lifecycle events.
147
   */
148
16
  void setListenerManager(Server::ListenerManager& listener_manager) {
149
16
    listener_manager_ = &listener_manager;
150
16
  }
151

            
152
  /**
153
   * Enables listener lifecycle event notifications. When enabled, the module will receive
154
   * on_bootstrap_extension_listener_add_or_update and on_bootstrap_extension_listener_removal
155
   * callbacks when listeners are added, updated, or removed.
156
   *
157
   * This must be called on the main thread after the server is initialized, since the
158
   * ListenerManager is not available during bootstrap extension creation.
159
   *
160
   * @return true if the callbacks were successfully registered, false if already registered.
161
   */
162
  bool enableListenerLifecycle();
163

            
164
  // Server::ListenerUpdateCallbacks
165
  void onListenerAddOrUpdate(absl::string_view listener_name,
166
                             const Network::ListenerConfig& listener_config) override;
167
  void onListenerRemoval(const std::string& listener_name) override;
168

            
169
  // The corresponding in-module configuration.
170
  envoy_dynamic_module_type_bootstrap_extension_config_module_ptr in_module_config_ = nullptr;
171

            
172
  // The function pointers for the module related to the bootstrap extension. All of them are
173
  // resolved during the construction of the config and made sure they are not nullptr after that.
174

            
175
  OnBootstrapExtensionConfigDestroyType on_bootstrap_extension_config_destroy_ = nullptr;
176
  OnBootstrapExtensionNewType on_bootstrap_extension_new_ = nullptr;
177
  OnBootstrapExtensionServerInitializedType on_bootstrap_extension_server_initialized_ = nullptr;
178
  OnBootstrapExtensionWorkerThreadInitializedType
179
      on_bootstrap_extension_worker_thread_initialized_ = nullptr;
180
  OnBootstrapExtensionDestroyType on_bootstrap_extension_destroy_ = nullptr;
181
  OnBootstrapExtensionDrainStartedType on_bootstrap_extension_drain_started_ = nullptr;
182
  OnBootstrapExtensionShutdownType on_bootstrap_extension_shutdown_ = nullptr;
183
  OnBootstrapExtensionConfigScheduledType on_bootstrap_extension_config_scheduled_ = nullptr;
184
  OnBootstrapExtensionHttpCalloutDoneType on_bootstrap_extension_http_callout_done_ = nullptr;
185
  OnBootstrapExtensionTimerFiredType on_bootstrap_extension_timer_fired_ = nullptr;
186
  OnBootstrapExtensionAdminRequestType on_bootstrap_extension_admin_request_ = nullptr;
187
  OnBootstrapExtensionClusterAddOrUpdateType on_bootstrap_extension_cluster_add_or_update_ =
188
      nullptr;
189
  OnBootstrapExtensionClusterRemovalType on_bootstrap_extension_cluster_removal_ = nullptr;
190
  OnBootstrapExtensionListenerAddOrUpdateType on_bootstrap_extension_listener_add_or_update_ =
191
      nullptr;
192
  OnBootstrapExtensionListenerRemovalType on_bootstrap_extension_listener_removal_ = nullptr;
193

            
194
  // The dynamic module.
195
  Extensions::DynamicModules::DynamicModulePtr dynamic_module_;
196

            
197
  // The main thread dispatcher.
198
  Event::Dispatcher& main_thread_dispatcher_;
199

            
200
  // The server factory context for accessing cluster manager lazily. ClusterManager is not
201
  // available during bootstrap extension creation, so we store the context and access it when
202
  // needed.
203
  Server::Configuration::ServerFactoryContext& context_;
204

            
205
  // The stats store for accessing metrics.
206
  Stats::Store& stats_store_;
207

            
208
  // The init target for blocking Envoy startup until the module signals readiness.
209
  // Created during config construction and registered with the init manager.
210
  std::unique_ptr<Init::TargetImpl> init_target_;
211

            
212
  // ----------------------------- Metrics Support -----------------------------
213
  // Handle classes for storing defined metrics. These follow the same pattern as the HTTP
214
  // filter config metrics support.
215

            
216
  class ModuleCounterHandle {
217
  public:
218
4
    ModuleCounterHandle(Stats::Counter& counter) : counter_(counter) {}
219
6
    void add(uint64_t value) const { counter_.add(value); }
220

            
221
  private:
222
    Stats::Counter& counter_;
223
  };
224

            
225
  class ModuleCounterVecHandle {
226
  public:
227
    ModuleCounterVecHandle(Stats::StatName name, Stats::StatNameVec label_names)
228
4
        : name_(name), label_names_(label_names) {}
229

            
230
6
    const Stats::StatNameVec& getLabelNames() const { return label_names_; }
231
2
    void add(Stats::Scope& scope, Stats::StatNameTagVectorOptConstRef tags, uint64_t amount) const {
232
2
      ASSERT(tags.has_value());
233
2
      Stats::Utility::counterFromElements(scope, {name_}, tags).add(amount);
234
2
    }
235

            
236
  private:
237
    Stats::StatName name_;
238
    Stats::StatNameVec label_names_;
239
  };
240

            
241
  class ModuleGaugeHandle {
242
  public:
243
4
    ModuleGaugeHandle(Stats::Gauge& gauge) : gauge_(gauge) {}
244
2
    void add(uint64_t value) const { gauge_.add(value); }
245
2
    void sub(uint64_t value) const { gauge_.sub(value); }
246
2
    void set(uint64_t value) const { gauge_.set(value); }
247

            
248
  private:
249
    Stats::Gauge& gauge_;
250
  };
251

            
252
  class ModuleGaugeVecHandle {
253
  public:
254
    ModuleGaugeVecHandle(Stats::StatName name, Stats::StatNameVec label_names,
255
                         Stats::Gauge::ImportMode import_mode)
256
3
        : name_(name), label_names_(label_names), import_mode_(import_mode) {}
257

            
258
15
    const Stats::StatNameVec& getLabelNames() const { return label_names_; }
259

            
260
2
    void add(Stats::Scope& scope, Stats::StatNameTagVectorOptConstRef tags, uint64_t amount) const {
261
2
      ASSERT(tags.has_value());
262
2
      Stats::Utility::gaugeFromElements(scope, {name_}, import_mode_, tags).add(amount);
263
2
    }
264
2
    void sub(Stats::Scope& scope, Stats::StatNameTagVectorOptConstRef tags, uint64_t amount) const {
265
2
      ASSERT(tags.has_value());
266
2
      Stats::Utility::gaugeFromElements(scope, {name_}, import_mode_, tags).sub(amount);
267
2
    }
268
2
    void set(Stats::Scope& scope, Stats::StatNameTagVectorOptConstRef tags, uint64_t amount) const {
269
2
      ASSERT(tags.has_value());
270
2
      Stats::Utility::gaugeFromElements(scope, {name_}, import_mode_, tags).set(amount);
271
2
    }
272

            
273
  private:
274
    Stats::StatName name_;
275
    Stats::StatNameVec label_names_;
276
    Stats::Gauge::ImportMode import_mode_;
277
  };
278

            
279
  class ModuleHistogramHandle {
280
  public:
281
2
    ModuleHistogramHandle(Stats::Histogram& histogram) : histogram_(histogram) {}
282
4
    void recordValue(uint64_t value) const { histogram_.recordValue(value); }
283

            
284
  private:
285
    Stats::Histogram& histogram_;
286
  };
287

            
288
  class ModuleHistogramVecHandle {
289
  public:
290
    ModuleHistogramVecHandle(Stats::StatName name, Stats::StatNameVec label_names,
291
                             Stats::Histogram::Unit unit)
292
3
        : name_(name), label_names_(label_names), unit_(unit) {}
293

            
294
5
    const Stats::StatNameVec& getLabelNames() const { return label_names_; }
295

            
296
    void recordValue(Stats::Scope& scope, Stats::StatNameTagVectorOptConstRef tags,
297
2
                     uint64_t value) const {
298
2
      ASSERT(tags.has_value());
299
2
      Stats::Utility::histogramFromElements(scope, {name_}, unit_, tags).recordValue(value);
300
2
    }
301

            
302
  private:
303
    Stats::StatName name_;
304
    Stats::StatNameVec label_names_;
305
    Stats::Histogram::Unit unit_;
306
  };
307

            
308
// We use 1-based IDs for the metrics in the ABI, so we need to convert them to 0-based indices
309
// for our internal storage. These helper functions do that conversion.
310
41
#define ID_TO_INDEX(id) ((id) - 1)
311

            
312
4
  size_t addCounter(ModuleCounterHandle&& counter) {
313
4
    counters_.push_back(std::move(counter));
314
4
    return counters_.size();
315
4
  }
316

            
317
4
  size_t addCounterVec(ModuleCounterVecHandle&& counter_vec) {
318
4
    counter_vecs_.push_back(std::move(counter_vec));
319
4
    return counter_vecs_.size();
320
4
  }
321

            
322
4
  size_t addGauge(ModuleGaugeHandle&& gauge) {
323
4
    gauges_.push_back(std::move(gauge));
324
4
    return gauges_.size();
325
4
  }
326

            
327
3
  size_t addGaugeVec(ModuleGaugeVecHandle&& gauge_vec) {
328
3
    gauge_vecs_.push_back(std::move(gauge_vec));
329
3
    return gauge_vecs_.size();
330
3
  }
331

            
332
2
  size_t addHistogram(ModuleHistogramHandle&& histogram) {
333
2
    histograms_.push_back(std::move(histogram));
334
2
    return histograms_.size();
335
2
  }
336

            
337
3
  size_t addHistogramVec(ModuleHistogramVecHandle&& histogram_vec) {
338
3
    histogram_vecs_.push_back(std::move(histogram_vec));
339
3
    return histogram_vecs_.size();
340
3
  }
341

            
342
11
  OptRef<const ModuleCounterHandle> getCounterById(size_t id) const {
343
11
    if (id == 0 || id > counters_.size()) {
344
2
      return {};
345
2
    }
346
9
    return counters_[ID_TO_INDEX(id)];
347
11
  }
348

            
349
7
  OptRef<const ModuleCounterVecHandle> getCounterVecById(size_t id) const {
350
7
    if (id == 0 || id > counter_vecs_.size()) {
351
2
      return {};
352
2
    }
353
5
    return counter_vecs_[ID_TO_INDEX(id)];
354
7
  }
355

            
356
13
  OptRef<const ModuleGaugeHandle> getGaugeById(size_t id) const {
357
13
    if (id == 0 || id > gauges_.size()) {
358
4
      return {};
359
4
    }
360
9
    return gauges_[ID_TO_INDEX(id)];
361
13
  }
362

            
363
13
  OptRef<const ModuleGaugeVecHandle> getGaugeVecById(size_t id) const {
364
13
    if (id == 0 || id > gauge_vecs_.size()) {
365
3
      return {};
366
3
    }
367
10
    return gauge_vecs_[ID_TO_INDEX(id)];
368
13
  }
369

            
370
5
  OptRef<const ModuleHistogramHandle> getHistogramById(size_t id) const {
371
5
    if (id == 0 || id > histograms_.size()) {
372
1
      return {};
373
1
    }
374
4
    return histograms_[ID_TO_INDEX(id)];
375
5
  }
376

            
377
5
  OptRef<const ModuleHistogramVecHandle> getHistogramVecById(size_t id) const {
378
5
    if (id == 0 || id > histogram_vecs_.size()) {
379
1
      return {};
380
1
    }
381
4
    return histogram_vecs_[ID_TO_INDEX(id)];
382
5
  }
383

            
384
#undef ID_TO_INDEX
385

            
386
  // Stats scope for metric creation.
387
  const Stats::ScopeSharedPtr stats_scope_;
388
  Stats::StatNamePool stat_name_pool_;
389

            
390
  // Temporary storage for the admin response body. Set by the
391
  // envoy_dynamic_module_callback_bootstrap_extension_admin_set_response callback during
392
  // on_bootstrap_extension_admin_request, then consumed by the admin handler lambda.
393
  std::string admin_response_body_;
394

            
395
private:
396
  /**
397
   * This implementation of the AsyncClient::Callbacks is used to handle the response from the HTTP
398
   * callout from the parent bootstrap extension config.
399
   */
400
  class HttpCalloutCallback : public Http::AsyncClient::Callbacks {
401
  public:
402
    HttpCalloutCallback(std::shared_ptr<DynamicModuleBootstrapExtensionConfig> config, uint64_t id)
403
7
        : config_(std::move(config)), callout_id_(id) {}
404
7
    ~HttpCalloutCallback() override = default;
405

            
406
    void onSuccess(const Http::AsyncClient::Request& request,
407
                   Http::ResponseMessagePtr&& response) override;
408
    void onFailure(const Http::AsyncClient::Request& request,
409
                   Http::AsyncClient::FailureReason reason) override;
410
    void onBeforeFinalizeUpstreamSpan(Envoy::Tracing::Span&,
411
1
                                      const Http::ResponseHeaderMap*) override {};
412

            
413
    // This is the request object that is used to send the HTTP callout. It is used to cancel the
414
    // callout if the config is destroyed before the callout is completed.
415
    Http::AsyncClient::Request* request_ = nullptr;
416

            
417
  private:
418
    const std::shared_ptr<DynamicModuleBootstrapExtensionConfig> config_;
419
    const uint64_t callout_id_{};
420
  };
421

            
422
7
  uint64_t getNextCalloutId() { return next_callout_id_++; }
423

            
424
  uint64_t next_callout_id_ = 1; // 0 is reserved as an invalid id.
425

            
426
  absl::flat_hash_map<uint64_t,
427
                      std::unique_ptr<DynamicModuleBootstrapExtensionConfig::HttpCalloutCallback>>
428
      http_callouts_;
429

            
430
  // Metric storage.
431
  std::vector<ModuleCounterHandle> counters_;
432
  std::vector<ModuleCounterVecHandle> counter_vecs_;
433
  std::vector<ModuleGaugeHandle> gauges_;
434
  std::vector<ModuleGaugeVecHandle> gauge_vecs_;
435
  std::vector<ModuleHistogramHandle> histograms_;
436
  std::vector<ModuleHistogramVecHandle> histogram_vecs_;
437

            
438
  // Cluster lifecycle callback handle. Set when the module enables cluster lifecycle events
439
  // via enableClusterLifecycle(). Reset during shutdown to avoid use-after-free since the
440
  // underlying TLS data is destroyed before the config.
441
  Upstream::ClusterUpdateCallbacksHandlePtr cluster_update_callbacks_handle_;
442
  // Handle for the shutdown lifecycle callback that cleans up cluster_update_callbacks_handle_.
443
  Server::ServerLifecycleNotifier::HandlePtr cluster_lifecycle_shutdown_handle_;
444
  bool cluster_lifecycle_enabled_ = false;
445

            
446
  // Listener manager pointer. Set during onServerInitialized via setListenerManager().
447
  // Not available during bootstrap extension creation.
448
  Server::ListenerManager* listener_manager_ = nullptr;
449

            
450
  // Listener lifecycle callback handle. Set when the module enables listener lifecycle events
451
  // via enableListenerLifecycle(). Reset during shutdown to avoid use-after-free since the
452
  // ListenerManager is destroyed before the config.
453
  Server::ListenerUpdateCallbacksHandlePtr listener_update_callbacks_handle_;
454
  // Handle for the shutdown lifecycle callback that cleans up listener_update_callbacks_handle_.
455
  Server::ServerLifecycleNotifier::HandlePtr listener_lifecycle_shutdown_handle_;
456
  bool listener_lifecycle_enabled_ = false;
457
};
458

            
459
using DynamicModuleBootstrapExtensionConfigSharedPtr =
460
    std::shared_ptr<DynamicModuleBootstrapExtensionConfig>;
461

            
462
/**
463
 * This class is used to schedule a bootstrap extension config event hook from a different thread
464
 * than the main thread. This is created via
465
 * envoy_dynamic_module_callback_bootstrap_extension_config_scheduler_new and deleted via
466
 * envoy_dynamic_module_callback_bootstrap_extension_config_scheduler_delete.
467
 */
468
class DynamicModuleBootstrapExtensionConfigScheduler {
469
public:
470
  DynamicModuleBootstrapExtensionConfigScheduler(
471
      std::weak_ptr<DynamicModuleBootstrapExtensionConfig> config, Event::Dispatcher& dispatcher)
472
7
      : config_(std::move(config)), dispatcher_(dispatcher) {}
473

            
474
6
  void commit(uint64_t event_id) {
475
6
    dispatcher_.post([config = config_, event_id]() {
476
6
      if (std::shared_ptr<DynamicModuleBootstrapExtensionConfig> config_shared = config.lock()) {
477
5
        config_shared->onScheduled(event_id);
478
5
      }
479
6
    });
480
6
  }
481

            
482
private:
483
  // The config that this scheduler is associated with. Using a weak pointer to avoid unnecessarily
484
  // extending the lifetime of the config.
485
  std::weak_ptr<DynamicModuleBootstrapExtensionConfig> config_;
486
  // The dispatcher is used to post the event to the main thread.
487
  Event::Dispatcher& dispatcher_;
488
};
489

            
490
/**
491
 * This class wraps an Envoy timer for use by bootstrap extension dynamic modules. It is created via
492
 * envoy_dynamic_module_callback_bootstrap_extension_timer_new and deleted via
493
 * envoy_dynamic_module_callback_bootstrap_extension_timer_delete.
494
 *
495
 * When the timer fires, it invokes the on_bootstrap_extension_timer_fired event hook on the main
496
 * thread if the config is still alive.
497
 */
498
class DynamicModuleBootstrapExtensionTimer {
499
public:
500
  explicit DynamicModuleBootstrapExtensionTimer(
501
      std::weak_ptr<DynamicModuleBootstrapExtensionConfig> config)
502
6
      : config_(std::move(config)) {}
503

            
504
  /**
505
   * Set the underlying Envoy timer. This is separated from construction to allow the timer
506
   * callback to capture a stable pointer to this object.
507
   */
508
6
  void setTimer(Event::TimerPtr timer) { timer_ = std::move(timer); }
509

            
510
12
  Event::Timer& timer() { return *timer_; }
511

            
512
private:
513
  // The config that this timer is associated with. Using a weak pointer to avoid unnecessarily
514
  // extending the lifetime of the config.
515
  std::weak_ptr<DynamicModuleBootstrapExtensionConfig> config_;
516
  // The underlying Envoy timer.
517
  Event::TimerPtr timer_;
518
};
519

            
520
/**
521
 * Creates a new DynamicModuleBootstrapExtensionConfig from the given module and configuration.
522
 * @param extension_name the name of the extension.
523
 * @param extension_config the configuration for the module.
524
 * @param metrics_namespace the namespace prefix for metrics emitted by this module.
525
 * @param dynamic_module the dynamic module to use.
526
 * @param main_thread_dispatcher the main thread dispatcher.
527
 * @param context the server factory context for accessing cluster manager lazily.
528
 * @param stats_store the stats store for accessing metrics.
529
 * @return an error status if the module could not be loaded or the configuration could not be
530
 * created, or a shared pointer to the config.
531
 */
532
absl::StatusOr<DynamicModuleBootstrapExtensionConfigSharedPtr>
533
newDynamicModuleBootstrapExtensionConfig(
534
    const absl::string_view extension_name, const absl::string_view extension_config,
535
    const absl::string_view metrics_namespace,
536
    Extensions::DynamicModules::DynamicModulePtr dynamic_module,
537
    Event::Dispatcher& main_thread_dispatcher, Server::Configuration::ServerFactoryContext& context,
538
    Stats::Store& stats_store);
539

            
540
} // namespace DynamicModules
541
} // namespace Bootstrap
542
} // namespace Extensions
543
} // namespace Envoy