Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/test/integration/server.h
Line
Count
Source (jump to first uncovered line)
1
#pragma once
2
3
#include <chrono>
4
#include <cstdint>
5
#include <list>
6
#include <memory>
7
#include <string>
8
9
#include "envoy/config/listener/v3/listener.pb.h"
10
#include "envoy/server/options.h"
11
#include "envoy/server/process_context.h"
12
#include "envoy/stats/histogram.h"
13
#include "envoy/stats/stats.h"
14
15
#include "source/common/common/assert.h"
16
#include "source/common/common/lock_guard.h"
17
#include "source/common/common/logger.h"
18
#include "source/common/common/thread.h"
19
#include "source/common/stats/allocator_impl.h"
20
#include "source/server/drain_manager_impl.h"
21
#include "source/server/listener_hooks.h"
22
#include "source/server/options_impl_base.h"
23
#include "source/server/server.h"
24
25
#include "test/integration/server_stats.h"
26
#include "test/integration/tcp_dump.h"
27
#include "test/test_common/test_time_system.h"
28
#include "test/test_common/utility.h"
29
30
#include "absl/synchronization/notification.h"
31
#include "absl/types/optional.h"
32
33
namespace Envoy {
34
namespace Server {
35
36
struct FieldValidationConfig {
37
  bool allow_unknown_static_fields = false;
38
  bool reject_unknown_dynamic_fields = false;
39
  bool ignore_unknown_dynamic_fields = false;
40
};
41
42
// Create OptionsImplBase structures suitable for tests. Disables hot restart.
43
OptionsImplBase createTestOptionsImpl(
44
    const std::string& config_path, const std::string& config_yaml,
45
    Network::Address::IpVersion ip_version,
46
    FieldValidationConfig validation_config = FieldValidationConfig(), uint32_t concurrency = 1,
47
    std::chrono::seconds drain_time = std::chrono::seconds(1),
48
    Server::DrainStrategy drain_strategy = Server::DrainStrategy::Gradual,
49
    bool use_bootstrap_node_metadata = false,
50
    std::unique_ptr<envoy::config::bootstrap::v3::Bootstrap>&& config_proto = nullptr);
51
52
class TestComponentFactory : public ComponentFactory {
53
public:
54
3.01k
  Server::DrainManagerPtr createDrainManager(Server::Instance& server) override {
55
3.01k
    return Server::DrainManagerPtr{new Server::DrainManagerImpl(
56
3.01k
        server, envoy::config::listener::v3::Listener::MODIFY_ONLY, server.dispatcher())};
57
3.01k
  }
58
  Runtime::LoaderPtr createRuntime(Server::Instance& server,
59
2.47k
                                   Server::Configuration::Initial& config) override {
60
2.47k
    return Server::InstanceUtil::createRuntime(server, config);
61
2.47k
  }
62
};
63
64
} // namespace Server
65
66
namespace Stats {
67
68
/**
69
 * This is a wrapper for Scopes for the TestIsolatedStoreImpl to ensure new scopes do
70
 * not interact with the store without grabbing the lock from TestIsolatedStoreImpl.
71
 */
72
class TestScopeWrapper : public Scope {
73
public:
74
  TestScopeWrapper(Thread::MutexBasicLockable& lock, ScopeSharedPtr wrapped_scope, Store& store)
75
39.2k
      : lock_(lock), wrapped_scope_(wrapped_scope), store_(store) {}
76
77
36.2k
  ScopeSharedPtr createScope(const std::string& name) override {
78
36.2k
    Thread::LockGuard lock(lock_);
79
36.2k
    return std::make_shared<TestScopeWrapper>(lock_, wrapped_scope_->createScope(name), store_);
80
36.2k
  }
81
82
0
  ScopeSharedPtr scopeFromStatName(StatName name) override {
83
0
    Thread::LockGuard lock(lock_);
84
0
    return std::make_shared<TestScopeWrapper>(lock_, wrapped_scope_->scopeFromStatName(name),
85
0
                                              store_);
86
0
  }
87
88
  Counter& counterFromStatNameWithTags(const StatName& name,
89
253k
                                       StatNameTagVectorOptConstRef tags) override {
90
253k
    Thread::LockGuard lock(lock_);
91
253k
    return wrapped_scope_->counterFromStatNameWithTags(name, tags);
92
253k
  }
93
94
  Gauge& gaugeFromStatNameWithTags(const StatName& name, StatNameTagVectorOptConstRef tags,
95
102k
                                   Gauge::ImportMode import_mode) override {
96
102k
    Thread::LockGuard lock(lock_);
97
102k
    return wrapped_scope_->gaugeFromStatNameWithTags(name, tags, import_mode);
98
102k
  }
99
100
  Histogram& histogramFromStatNameWithTags(const StatName& name, StatNameTagVectorOptConstRef tags,
101
12.5k
                                           Histogram::Unit unit) override {
102
12.5k
    Thread::LockGuard lock(lock_);
103
12.5k
    return wrapped_scope_->histogramFromStatNameWithTags(name, tags, unit);
104
12.5k
  }
105
106
  TextReadout& textReadoutFromStatNameWithTags(const StatName& name,
107
3.73k
                                               StatNameTagVectorOptConstRef tags) override {
108
3.73k
    Thread::LockGuard lock(lock_);
109
3.73k
    return wrapped_scope_->textReadoutFromStatNameWithTags(name, tags);
110
3.73k
  }
111
112
220k
  Counter& counterFromString(const std::string& name) override {
113
220k
    StatNameManagedStorage storage(name, symbolTable());
114
220k
    return counterFromStatName(storage.statName());
115
220k
  }
116
102k
  Gauge& gaugeFromString(const std::string& name, Gauge::ImportMode import_mode) override {
117
102k
    StatNameManagedStorage storage(name, symbolTable());
118
102k
    return gaugeFromStatName(storage.statName(), import_mode);
119
102k
  }
120
10.0k
  Histogram& histogramFromString(const std::string& name, Histogram::Unit unit) override {
121
10.0k
    StatNameManagedStorage storage(name, symbolTable());
122
10.0k
    return histogramFromStatName(storage.statName(), unit);
123
10.0k
  }
124
3.73k
  TextReadout& textReadoutFromString(const std::string& name) override {
125
3.73k
    StatNameManagedStorage storage(name, symbolTable());
126
3.73k
    return textReadoutFromStatName(storage.statName());
127
3.73k
  }
128
129
0
  CounterOptConstRef findCounter(StatName name) const override {
130
0
    Thread::LockGuard lock(lock_);
131
0
    return wrapped_scope_->findCounter(name);
132
0
  }
133
0
  GaugeOptConstRef findGauge(StatName name) const override {
134
0
    Thread::LockGuard lock(lock_);
135
0
    return wrapped_scope_->findGauge(name);
136
0
  }
137
0
  HistogramOptConstRef findHistogram(StatName name) const override {
138
0
    Thread::LockGuard lock(lock_);
139
0
    return wrapped_scope_->findHistogram(name);
140
0
  }
141
0
  TextReadoutOptConstRef findTextReadout(StatName name) const override {
142
0
    Thread::LockGuard lock(lock_);
143
0
    return wrapped_scope_->findTextReadout(name);
144
0
  }
145
146
0
  const SymbolTable& constSymbolTable() const override {
147
0
    return wrapped_scope_->constSymbolTable();
148
0
  }
149
348k
  SymbolTable& symbolTable() override { return wrapped_scope_->symbolTable(); }
150
151
0
  bool iterate(const IterateFn<Counter>& fn) const override { return wrapped_scope_->iterate(fn); }
152
0
  bool iterate(const IterateFn<Gauge>& fn) const override { return wrapped_scope_->iterate(fn); }
153
0
  bool iterate(const IterateFn<Histogram>& fn) const override {
154
0
    return wrapped_scope_->iterate(fn);
155
0
  }
156
0
  bool iterate(const IterateFn<TextReadout>& fn) const override {
157
0
    return wrapped_scope_->iterate(fn);
158
0
  }
159
0
  StatName prefix() const override { return wrapped_scope_->prefix(); }
160
0
  Store& store() override { return store_; }
161
0
  const Store& constStore() const override { return store_; }
162
163
private:
164
  Thread::MutexBasicLockable& lock_;
165
  ScopeSharedPtr wrapped_scope_;
166
  Store& store_;
167
};
168
169
// A counter which signals on a condition variable when it is incremented.
170
class NotifyingCounter : public Stats::Counter {
171
public:
172
  NotifyingCounter(Stats::Counter* counter, absl::Mutex& mutex, absl::CondVar& condvar)
173
637k
      : counter_(counter), mutex_(mutex), condvar_(condvar) {}
174
175
2.64M
  std::string name() const override { return counter_->name(); }
176
5.68M
  StatName statName() const override { return counter_->statName(); }
177
0
  TagVector tags() const override { return counter_->tags(); }
178
0
  std::string tagExtractedName() const override { return counter_->tagExtractedName(); }
179
0
  void iterateTagStatNames(const TagStatNameIterFn& fn) const override {
180
0
    counter_->iterateTagStatNames(fn);
181
0
  }
182
109k
  void add(uint64_t amount) override {
183
109k
    counter_->add(amount);
184
109k
    absl::MutexLock l(&mutex_);
185
109k
    condvar_.Signal();
186
109k
  }
187
83.1k
  void inc() override { add(1); }
188
638k
  uint64_t latch() override { return counter_->latch(); }
189
0
  void reset() override { return counter_->reset(); }
190
6.46k
  uint64_t value() const override { return counter_->value(); }
191
4.48M
  void incRefCount() override { counter_->incRefCount(); }
192
4.48M
  bool decRefCount() override { return counter_->decRefCount(); }
193
0
  uint32_t use_count() const override { return counter_->use_count(); }
194
0
  StatName tagExtractedStatName() const override { return counter_->tagExtractedStatName(); }
195
0
  bool used() const override { return counter_->used(); }
196
0
  bool hidden() const override { return counter_->hidden(); }
197
0
  SymbolTable& symbolTable() override { return counter_->symbolTable(); }
198
0
  const SymbolTable& constSymbolTable() const override { return counter_->constSymbolTable(); }
199
200
private:
201
  std::unique_ptr<Stats::Counter> counter_;
202
  absl::Mutex& mutex_;
203
  absl::CondVar& condvar_;
204
};
205
206
// A stats allocator which creates NotifyingCounters rather than regular CounterImpls.
207
class NotifyingAllocatorImpl : public Stats::AllocatorImpl {
208
public:
209
  using Stats::AllocatorImpl::AllocatorImpl;
210
211
0
  void waitForCounterFromStringEq(const std::string& name, uint64_t value) {
212
0
    absl::MutexLock l(&mutex_);
213
0
    ENVOY_LOG_MISC(trace, "waiting for {} to be {}", name, value);
214
0
    while (getCounterLockHeld(name) == nullptr || getCounterLockHeld(name)->value() != value) {
215
0
      condvar_.Wait(&mutex_);
216
0
    }
217
0
    ENVOY_LOG_MISC(trace, "done waiting for {} to be {}", name, value);
218
0
  }
219
220
0
  void waitForCounterFromStringGe(const std::string& name, uint64_t value) {
221
0
    absl::MutexLock l(&mutex_);
222
0
    ENVOY_LOG_MISC(trace, "waiting for {} to be {}", name, value);
223
0
    while (getCounterLockHeld(name) == nullptr || getCounterLockHeld(name)->value() < value) {
224
0
      condvar_.Wait(&mutex_);
225
0
    }
226
0
    ENVOY_LOG_MISC(trace, "done waiting for {} to be {}", name, value);
227
0
  }
228
229
0
  void waitForCounterExists(const std::string& name) {
230
0
    absl::MutexLock l(&mutex_);
231
0
    ENVOY_LOG_MISC(trace, "waiting for {} to exist", name);
232
0
    while (getCounterLockHeld(name) == nullptr) {
233
0
      condvar_.Wait(&mutex_);
234
0
    }
235
0
    ENVOY_LOG_MISC(trace, "done waiting for {} to exist", name);
236
0
  }
237
238
protected:
239
  Stats::Counter* makeCounterInternal(StatName name, StatName tag_extracted_name,
240
637k
                                      const StatNameTagVector& stat_name_tags) override {
241
637k
    Stats::Counter* counter = new NotifyingCounter(
242
637k
        Stats::AllocatorImpl::makeCounterInternal(name, tag_extracted_name, stat_name_tags), mutex_,
243
637k
        condvar_);
244
637k
    {
245
637k
      absl::MutexLock l(&mutex_);
246
      // Allow getting the counter directly from the allocator, since it's harder to
247
      // signal when the counter has been added to a given stats store.
248
637k
      counters_.emplace(counter->name(), counter);
249
637k
      if (counter->name() == "cluster_manager.cluster_removed") {
250
1.97k
      }
251
637k
      condvar_.Signal();
252
637k
    }
253
637k
    return counter;
254
637k
  }
255
256
  virtual Stats::Counter* getCounterLockHeld(const std::string& name)
257
0
      ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
258
0
    auto it = counters_.find(name);
259
0
    if (it != counters_.end()) {
260
0
      return it->second;
261
0
    }
262
0
    return nullptr;
263
0
  }
264
265
private:
266
  absl::flat_hash_map<std::string, Stats::Counter*> counters_;
267
  absl::Mutex mutex_;
268
  absl::CondVar condvar_;
269
};
270
271
/**
272
 * This is a variant of the isolated store that has locking across all operations so that it can
273
 * be used during the integration tests.
274
 */
275
class TestIsolatedStoreImpl : public StoreRoot {
276
public:
277
  // Stats::Store
278
0
  void forEachCounter(Stats::SizeFn f_size, StatFn<Counter> f_stat) const override {
279
0
    Thread::LockGuard lock(lock_);
280
0
    store_.forEachCounter(f_size, f_stat);
281
0
  }
282
0
  void forEachGauge(Stats::SizeFn f_size, StatFn<Gauge> f_stat) const override {
283
0
    Thread::LockGuard lock(lock_);
284
0
    store_.forEachGauge(f_size, f_stat);
285
0
  }
286
0
  void forEachTextReadout(Stats::SizeFn f_size, StatFn<TextReadout> f_stat) const override {
287
0
    Thread::LockGuard lock(lock_);
288
0
    store_.forEachTextReadout(f_size, f_stat);
289
0
  }
290
0
  void forEachHistogram(Stats::SizeFn f_size, StatFn<ParentHistogram> f_stat) const override {
291
0
    Thread::LockGuard lock(lock_);
292
0
    store_.forEachHistogram(f_size, f_stat);
293
0
  }
294
  void forEachScope(std::function<void(std::size_t)> f_size,
295
0
                    StatFn<const Scope> f_scope) const override {
296
0
    Thread::LockGuard lock(lock_);
297
0
    store_.forEachScope(f_size, f_scope);
298
0
  }
299
1.05k
  void forEachSinkedCounter(Stats::SizeFn f_size, StatFn<Counter> f_stat) const override {
300
1.05k
    Thread::LockGuard lock(lock_);
301
1.05k
    store_.forEachSinkedCounter(f_size, f_stat);
302
1.05k
  }
303
1.05k
  void forEachSinkedGauge(Stats::SizeFn f_size, StatFn<Gauge> f_stat) const override {
304
1.05k
    Thread::LockGuard lock(lock_);
305
1.05k
    store_.forEachSinkedGauge(f_size, f_stat);
306
1.05k
  }
307
1.05k
  void forEachSinkedTextReadout(Stats::SizeFn f_size, StatFn<TextReadout> f_stat) const override {
308
1.05k
    Thread::LockGuard lock(lock_);
309
1.05k
    store_.forEachSinkedTextReadout(f_size, f_stat);
310
1.05k
  }
311
1.05k
  void forEachSinkedHistogram(Stats::SizeFn f_size, StatFn<ParentHistogram> f_stat) const override {
312
1.05k
    Thread::LockGuard lock(lock_);
313
1.05k
    store_.forEachSinkedHistogram(f_size, f_stat);
314
1.05k
  }
315
0
  void setSinkPredicates(std::unique_ptr<SinkPredicates>&& sink_predicates) override {
316
0
    UNREFERENCED_PARAMETER(sink_predicates);
317
0
  }
318
0
  OptRef<SinkPredicates> sinkPredicates() override { return OptRef<SinkPredicates>{}; }
319
0
  void deliverHistogramToSinks(const Histogram& histogram, uint64_t value) override {
320
0
    Thread::LockGuard lock(lock_);
321
0
    store_.deliverHistogramToSinks(histogram, value);
322
0
  }
323
0
  NullGaugeImpl& nullGauge() override { return store_.nullGauge(); }
324
0
  NullCounterImpl& nullCounter() override { return store_.nullCounter(); }
325
136k
  ScopeSharedPtr rootScope() override {
326
136k
    Thread::LockGuard lock(lock_);
327
136k
    if (lazy_default_scope_ == nullptr) {
328
3.01k
      lazy_default_scope_ = std::make_shared<TestScopeWrapper>(lock_, store_.rootScope(), *this);
329
3.01k
    }
330
136k
    return lazy_default_scope_;
331
136k
  }
332
0
  ConstScopeSharedPtr constRootScope() const override {
333
0
    return const_cast<TestIsolatedStoreImpl*>(this)->rootScope();
334
0
  }
335
0
  const SymbolTable& constSymbolTable() const override { return store_.constSymbolTable(); }
336
37.1k
  SymbolTable& symbolTable() override { return store_.symbolTable(); }
337
338
  // Stats::Store
339
0
  std::vector<CounterSharedPtr> counters() const override {
340
0
    Thread::LockGuard lock(lock_);
341
0
    return store_.counters();
342
0
  }
343
0
  std::vector<GaugeSharedPtr> gauges() const override {
344
0
    Thread::LockGuard lock(lock_);
345
0
    return store_.gauges();
346
0
  }
347
0
  std::vector<ParentHistogramSharedPtr> histograms() const override {
348
0
    Thread::LockGuard lock(lock_);
349
0
    return store_.histograms();
350
0
  }
351
0
  std::vector<TextReadoutSharedPtr> textReadouts() const override {
352
0
    Thread::LockGuard lock(lock_);
353
0
    return store_.textReadouts();
354
0
  }
355
356
0
  bool iterate(const IterateFn<Counter>& fn) const override { return store_.iterate(fn); }
357
0
  bool iterate(const IterateFn<Gauge>& fn) const override { return store_.iterate(fn); }
358
0
  bool iterate(const IterateFn<Histogram>& fn) const override { return store_.iterate(fn); }
359
0
  bool iterate(const IterateFn<TextReadout>& fn) const override { return store_.iterate(fn); }
360
361
0
  void extractAndAppendTags(StatName, StatNamePool&, StatNameTagVector&) override{};
362
0
  void extractAndAppendTags(absl::string_view, StatNamePool&, StatNameTagVector&) override{};
363
0
  const Stats::TagVector& fixedTags() override { CONSTRUCT_ON_FIRST_USE(Stats::TagVector); }
364
365
  // Stats::StoreRoot
366
0
  void addSink(Sink&) override {}
367
2.62k
  void setTagProducer(TagProducerPtr&&) override {}
368
2.55k
  void setStatsMatcher(StatsMatcherPtr&&) override {}
369
2.49k
  void setHistogramSettings(HistogramSettingsConstPtr&&) override {}
370
2.47k
  void initializeThreading(Event::Dispatcher&, ThreadLocal::Instance&) override {}
371
3.01k
  void shutdownThreading() override {}
372
0
  void mergeHistograms(PostMergeCb cb) override { merge_cb_ = cb; }
373
374
0
  void runMergeCallback() { merge_cb_(); }
375
376
private:
377
  mutable Thread::MutexBasicLockable lock_;
378
  IsolatedStoreImpl store_;
379
  PostMergeCb merge_cb_;
380
  ScopeSharedPtr lazy_default_scope_;
381
};
382
383
} // namespace Stats
384
385
class IntegrationTestServer;
386
using IntegrationTestServerPtr = std::unique_ptr<IntegrationTestServer>;
387
388
/**
389
 * Wrapper for running the real server for the purpose of integration tests.
390
 * This class is an Abstract Base Class and delegates ownership and management
391
 * of the actual envoy server to a derived class. See the documentation for
392
 * createAndRunEnvoyServer().
393
 */
394
class IntegrationTestServer : public Logger::Loggable<Logger::Id::testing>,
395
                              public ListenerHooks,
396
                              public IntegrationTestServerStats,
397
                              public Server::ComponentFactory {
398
public:
399
  static IntegrationTestServerPtr
400
  create(const std::string& config_path, const Network::Address::IpVersion version,
401
         std::function<void(IntegrationTestServer&)> on_server_ready_function,
402
         std::function<void()> on_server_init_function,
403
         absl::optional<uint64_t> deterministic_value, Event::TestTimeSystem& time_system,
404
         Api::Api& api, bool defer_listener_finalization = false,
405
         ProcessObjectOptRef process_object = absl::nullopt,
406
         Server::FieldValidationConfig validation_config = Server::FieldValidationConfig(),
407
         uint32_t concurrency = 1, std::chrono::seconds drain_time = std::chrono::seconds(1),
408
         Server::DrainStrategy drain_strategy = Server::DrainStrategy::Gradual,
409
         Buffer::WatermarkFactorySharedPtr watermark_factory = nullptr, bool use_real_stats = false,
410
         bool use_bootstrap_node_metadata = false,
411
         std::unique_ptr<envoy::config::bootstrap::v3::Bootstrap>&& config_proto = nullptr);
412
  // Note that the derived class is responsible for tearing down the server in its
413
  // destructor.
414
  ~IntegrationTestServer() override;
415
416
  void waitUntilListenersReady();
417
418
  void setDynamicContextParam(absl::string_view resource_type_url, absl::string_view key,
419
                              absl::string_view value);
420
  void unsetDynamicContextParam(absl::string_view resource_type_url, absl::string_view key);
421
422
0
  Server::DrainManagerImpl& drainManager() { return *drain_manager_; }
423
0
  void setOnWorkerListenerAddedCb(std::function<void()> on_worker_listener_added) {
424
0
    on_worker_listener_added_cb_ = std::move(on_worker_listener_added);
425
0
  }
426
0
  void setOnWorkerListenerRemovedCb(std::function<void()> on_worker_listener_removed) {
427
0
    on_worker_listener_removed_cb_ = std::move(on_worker_listener_removed);
428
0
  }
429
0
  void setOnServerReadyCb(std::function<void(IntegrationTestServer&)> on_server_ready) {
430
0
    on_server_ready_cb_ = std::move(on_server_ready);
431
0
  }
432
1.97k
  void onWorkersStarted() override {}
433
434
  void start(const Network::Address::IpVersion version,
435
             std::function<void()> on_server_init_function,
436
             absl::optional<uint64_t> deterministic_value, bool defer_listener_finalization,
437
             ProcessObjectOptRef process_object, Server::FieldValidationConfig validation_config,
438
             uint32_t concurrency, std::chrono::seconds drain_time,
439
             Server::DrainStrategy drain_strategy,
440
             Buffer::WatermarkFactorySharedPtr watermark_factory, bool use_bootstrap_node_metadata);
441
442
  void waitForCounterEq(const std::string& name, uint64_t value,
443
                        std::chrono::milliseconds timeout = TestUtility::DefaultTimeout,
444
550
                        Event::Dispatcher* dispatcher = nullptr) override {
445
550
    ASSERT_TRUE(
446
550
        TestUtility::waitForCounterEq(statStore(), name, value, time_system_, timeout, dispatcher));
447
550
  }
448
449
  void waitForCounterGe(const std::string& name, uint64_t value,
450
0
                        std::chrono::milliseconds timeout = TestUtility::DefaultTimeout) override {
451
0
    ASSERT_TRUE(TestUtility::waitForCounterGe(statStore(), name, value, time_system_, timeout));
452
0
  }
453
454
  void waitForGaugeEq(const std::string& name, uint64_t value,
455
402
                      std::chrono::milliseconds timeout = TestUtility::DefaultTimeout) override {
456
402
    ASSERT_TRUE(TestUtility::waitForGaugeEq(statStore(), name, value, time_system_, timeout));
457
402
  }
458
459
  void waitForGaugeGe(const std::string& name, uint64_t value,
460
0
                      std::chrono::milliseconds timeout = TestUtility::DefaultTimeout) override {
461
0
    ASSERT_TRUE(TestUtility::waitForGaugeGe(statStore(), name, value, time_system_, timeout));
462
0
  }
463
464
0
  void waitForCounterExists(const std::string& name) override {
465
0
    notifyingStatsAllocator().waitForCounterExists(name);
466
0
  }
467
468
  void waitForCounterNonexistent(const std::string& name,
469
0
                                 std::chrono::milliseconds timeout) override {
470
0
    Event::TestTimeSystem::RealTimeBound bound(timeout);
471
0
    while (TestUtility::findCounter(statStore(), name) != nullptr) {
472
0
      time_system_.advanceTimeWait(std::chrono::milliseconds(10));
473
0
      ASSERT_FALSE(!bound.withinBound())
474
0
          << "timed out waiting for counter " << name << " to not exist.";
475
0
    }
476
0
  }
477
478
  void waitForProactiveOverloadResourceUsageEq(
479
      const Server::OverloadProactiveResourceName resource_name, int64_t value,
480
      Event::Dispatcher& dispatcher,
481
0
      std::chrono::milliseconds timeout = TestUtility::DefaultTimeout) {
482
0
    ASSERT_TRUE(TestUtility::waitForProactiveOverloadResourceUsageEq(
483
0
        overloadState(), resource_name, value, time_system_, dispatcher, timeout));
484
0
  }
485
486
  // TODO(#17956): Add Gauge type to NotifyingAllocator and adopt it in this method.
487
0
  void waitForGaugeDestroyed(const std::string& name) override {
488
0
    ASSERT_TRUE(TestUtility::waitForGaugeDestroyed(statStore(), name, time_system_));
489
0
  }
490
491
  void waitUntilHistogramHasSamples(
492
      const std::string& name,
493
0
      std::chrono::milliseconds timeout = std::chrono::milliseconds::zero()) override {
494
0
    waitForNumHistogramSamplesGe(name, 1, timeout);
495
0
  }
496
497
  void waitForNumHistogramSamplesGe(
498
      const std::string& name, uint64_t sample_count,
499
0
      std::chrono::milliseconds timeout = std::chrono::milliseconds::zero()) override {
500
0
    ASSERT_TRUE(TestUtility::waitForNumHistogramSamplesGe(
501
0
        statStore(), name, sample_count, time_system_, server().dispatcher(), timeout));
502
0
  }
503
504
7.86k
  Stats::CounterSharedPtr counter(const std::string& name) override {
505
    // When using the thread local store, only counters() is thread safe. This also allows us
506
    // to test if a counter exists at all versus just defaulting to zero.
507
7.86k
    return TestUtility::findCounter(statStore(), name);
508
7.86k
  }
509
510
42
  Stats::GaugeSharedPtr gauge(const std::string& name) override {
511
    // When using the thread local store, only gauges() is thread safe. This also allows us
512
    // to test if a counter exists at all versus just defaulting to zero.
513
42
    return TestUtility::findGauge(statStore(), name);
514
42
  }
515
516
0
  Stats::ParentHistogramSharedPtr histogram(const std::string& name) {
517
0
    return TestUtility::findHistogram(statStore(), name);
518
0
  }
519
520
0
  std::vector<Stats::CounterSharedPtr> counters() override { return statStore().counters(); }
521
522
0
  std::vector<Stats::GaugeSharedPtr> gauges() override { return statStore().gauges(); }
523
524
0
  std::vector<Stats::ParentHistogramSharedPtr> histograms() override {
525
0
    return statStore().histograms();
526
0
  }
527
528
  // ListenerHooks
529
  void onWorkerListenerAdded() override;
530
  void onWorkerListenerRemoved() override;
531
532
  // Server::ComponentFactory
533
1.97k
  Server::DrainManagerPtr createDrainManager(Server::Instance& server) override {
534
1.97k
    drain_manager_ = new Server::DrainManagerImpl(
535
1.97k
        server, envoy::config::listener::v3::Listener::MODIFY_ONLY, server.dispatcher());
536
1.97k
    return Server::DrainManagerPtr{drain_manager_};
537
1.97k
  }
538
  Runtime::LoaderPtr createRuntime(Server::Instance& server,
539
1.97k
                                   Server::Configuration::Initial& config) override {
540
1.97k
    return Server::InstanceUtil::createRuntime(server, config);
541
1.97k
  }
542
543
  // Should not be called until createAndRunEnvoyServer() is called.
544
  virtual Server::Instance& server() PURE;
545
  virtual Stats::Store& statStore() PURE;
546
  virtual Server::ThreadLocalOverloadState& overloadState() PURE;
547
  virtual Network::Address::InstanceConstSharedPtr adminAddress() PURE;
548
  virtual Stats::NotifyingAllocatorImpl& notifyingStatsAllocator() PURE;
549
0
  void useAdminInterfaceToQuit(bool use) { use_admin_interface_to_quit_ = use; }
550
1.97k
  bool useAdminInterfaceToQuit() { return use_admin_interface_to_quit_; }
551
552
protected:
553
  IntegrationTestServer(Event::TestTimeSystem& time_system, Api::Api& api,
554
                        const std::string& config_path,
555
                        std::unique_ptr<envoy::config::bootstrap::v3::Bootstrap>&& config_proto)
556
      : time_system_(time_system), api_(api), config_path_(config_path),
557
1.97k
        config_proto_(std::move(config_proto)) {}
558
559
  // Create the running envoy server. This function will call serverReady() when the virtual
560
  // functions server(), statStore(), and adminAddress() may be called, but before the server
561
  // has been started.
562
  // The subclass is also responsible for tearing down this server in its destructor.
563
  virtual void createAndRunEnvoyServer(OptionsImplBase& options, Event::TimeSystem& time_system,
564
                                       Network::Address::InstanceConstSharedPtr local_address,
565
                                       ListenerHooks& hooks, Thread::BasicLockable& access_log_lock,
566
                                       Server::ComponentFactory& component_factory,
567
                                       Random::RandomGeneratorPtr&& random_generator,
568
                                       ProcessObjectOptRef process_object,
569
                                       Buffer::WatermarkFactorySharedPtr watermark_factory) PURE;
570
571
  // Will be called by subclass on server thread when the server is ready to be accessed. The
572
  // server may not have been run yet, but all server access methods (server(), statStore(),
573
  // adminAddress()) will be available.
574
  void serverReady();
575
576
private:
577
  /**
578
   * Runs the real server on a thread.
579
   */
580
  void threadRoutine(const Network::Address::IpVersion version,
581
                     absl::optional<uint64_t> deterministic_value,
582
                     ProcessObjectOptRef process_object,
583
                     Server::FieldValidationConfig validation_config, uint32_t concurrency,
584
                     std::chrono::seconds drain_time, Server::DrainStrategy drain_strategy,
585
                     Buffer::WatermarkFactorySharedPtr watermark_factory,
586
                     bool use_bootstrap_node_metadata);
587
588
  Event::TestTimeSystem& time_system_;
589
  Api::Api& api_;
590
  const std::string config_path_;
591
  Thread::ThreadPtr thread_;
592
  Thread::CondVar listeners_cv_;
593
  Thread::MutexBasicLockable listeners_mutex_;
594
  uint64_t pending_listeners_;
595
  ConditionalInitializer server_set_;
596
  Server::DrainManagerImpl* drain_manager_{};
597
  std::function<void()> on_worker_listener_added_cb_;
598
  std::function<void()> on_worker_listener_removed_cb_;
599
  TcpDumpPtr tcp_dump_;
600
  std::function<void(IntegrationTestServer&)> on_server_ready_cb_;
601
  bool use_admin_interface_to_quit_{};
602
  std::unique_ptr<envoy::config::bootstrap::v3::Bootstrap> config_proto_;
603
};
604
605
// Default implementation of IntegrationTestServer
606
class IntegrationTestServerImpl : public IntegrationTestServer {
607
public:
608
  IntegrationTestServerImpl(
609
      Event::TestTimeSystem& time_system, Api::Api& api, const std::string& config_path,
610
      bool real_stats = false,
611
      std::unique_ptr<envoy::config::bootstrap::v3::Bootstrap>&& config_proto = nullptr);
612
613
  ~IntegrationTestServerImpl() override;
614
615
9.85k
  Server::Instance& server() override {
616
9.85k
    RELEASE_ASSERT(server_ != nullptr, "");
617
9.85k
    return *server_;
618
9.85k
  }
619
8.85k
  Stats::Store& statStore() override {
620
8.85k
    RELEASE_ASSERT(stat_store_ != nullptr, "");
621
8.85k
    return *stat_store_;
622
8.85k
  }
623
0
  Server::ThreadLocalOverloadState& overloadState() override {
624
0
    RELEASE_ASSERT(server_ != nullptr, "");
625
0
    return server_->overloadManager().getThreadLocalOverloadState();
626
0
  }
627
628
0
  Network::Address::InstanceConstSharedPtr adminAddress() override { return admin_address_; }
629
630
0
  Stats::NotifyingAllocatorImpl& notifyingStatsAllocator() override {
631
0
    auto* ret = dynamic_cast<Stats::NotifyingAllocatorImpl*>(stats_allocator_.get());
632
0
    RELEASE_ASSERT(ret != nullptr,
633
0
                   "notifyingStatsAllocator() is not created when real_stats is true");
634
0
    return *ret;
635
0
  }
636
637
private:
638
  void createAndRunEnvoyServer(OptionsImplBase& options, Event::TimeSystem& time_system,
639
                               Network::Address::InstanceConstSharedPtr local_address,
640
                               ListenerHooks& hooks, Thread::BasicLockable& access_log_lock,
641
                               Server::ComponentFactory& component_factory,
642
                               Random::RandomGeneratorPtr&& random_generator,
643
                               ProcessObjectOptRef process_object,
644
                               Buffer::WatermarkFactorySharedPtr watermark_factory) override;
645
646
  // Owned by this class. An owning pointer is not used because the actual allocation is done
647
  // on a stack in a non-main thread.
648
  Server::Instance* server_{};
649
  Stats::Store* stat_store_{};
650
  Network::Address::InstanceConstSharedPtr admin_address_;
651
  absl::Notification server_gone_;
652
  Stats::SymbolTableImpl symbol_table_;
653
  std::unique_ptr<Stats::AllocatorImpl> stats_allocator_;
654
};
655
656
} // namespace Envoy