Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/server/admin/clusters_handler.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/server/admin/clusters_handler.h"
2
3
#include "envoy/admin/v3/clusters.pb.h"
4
5
#include "source/common/http/headers.h"
6
#include "source/common/http/utility.h"
7
#include "source/common/network/utility.h"
8
#include "source/common/upstream/host_utility.h"
9
#include "source/server/admin/utils.h"
10
11
namespace Envoy {
12
namespace Server {
13
14
namespace {
15
16
void addCircuitBreakerSettingsAsText(const std::string& cluster_name,
17
                                     const std::string& priority_str,
18
                                     Upstream::ResourceManager& resource_manager,
19
0
                                     Buffer::Instance& response) {
20
0
  response.add(fmt::format("{}::{}_priority::max_connections::{}\n", cluster_name, priority_str,
21
0
                           resource_manager.connections().max()));
22
0
  response.add(fmt::format("{}::{}_priority::max_pending_requests::{}\n", cluster_name,
23
0
                           priority_str, resource_manager.pendingRequests().max()));
24
0
  response.add(fmt::format("{}::{}_priority::max_requests::{}\n", cluster_name, priority_str,
25
0
                           resource_manager.requests().max()));
26
0
  response.add(fmt::format("{}::{}_priority::max_retries::{}\n", cluster_name, priority_str,
27
0
                           resource_manager.retries().max()));
28
0
}
29
30
void addCircuitBreakerSettingsAsJson(const envoy::config::core::v3::RoutingPriority& priority,
31
                                     Upstream::ResourceManager& resource_manager,
32
0
                                     envoy::admin::v3::ClusterStatus& cluster_status) {
33
0
  auto& thresholds = *cluster_status.mutable_circuit_breakers()->add_thresholds();
34
0
  thresholds.set_priority(priority);
35
0
  thresholds.mutable_max_connections()->set_value(resource_manager.connections().max());
36
0
  thresholds.mutable_max_pending_requests()->set_value(resource_manager.pendingRequests().max());
37
0
  thresholds.mutable_max_requests()->set_value(resource_manager.requests().max());
38
0
  thresholds.mutable_max_retries()->set_value(resource_manager.retries().max());
39
0
}
40
41
} // namespace
42
43
5.34k
ClustersHandler::ClustersHandler(Server::Instance& server) : HandlerContextBase(server) {}
44
45
Http::Code ClustersHandler::handlerClusters(Http::ResponseHeaderMap& response_headers,
46
0
                                            Buffer::Instance& response, AdminStream& admin_stream) {
47
0
  const auto format_value = Utility::formatParam(admin_stream.queryParams());
48
49
0
  if (format_value.has_value() && format_value.value() == "json") {
50
0
    writeClustersAsJson(response);
51
0
    response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json);
52
0
  } else {
53
0
    writeClustersAsText(response);
54
0
  }
55
56
0
  return Http::Code::OK;
57
0
}
58
59
// Helper method that ensures that we've setting flags based on all the health flag values on the
60
// host.
61
void setHealthFlag(Upstream::Host::HealthFlag flag, const Upstream::Host& host,
62
0
                   envoy::admin::v3::HostHealthStatus& health_status) {
63
0
  switch (flag) {
64
0
  case Upstream::Host::HealthFlag::FAILED_ACTIVE_HC:
65
0
    health_status.set_failed_active_health_check(
66
0
        host.healthFlagGet(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC));
67
0
    break;
68
0
  case Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK:
69
0
    health_status.set_failed_outlier_check(
70
0
        host.healthFlagGet(Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK));
71
0
    break;
72
0
  case Upstream::Host::HealthFlag::FAILED_EDS_HEALTH:
73
0
  case Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH:
74
0
    if (host.healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH)) {
75
0
      health_status.set_eds_health_status(envoy::config::core::v3::UNHEALTHY);
76
0
    } else if (host.healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH)) {
77
0
      health_status.set_eds_health_status(envoy::config::core::v3::DEGRADED);
78
0
    } else {
79
0
      health_status.set_eds_health_status(envoy::config::core::v3::HEALTHY);
80
0
    }
81
0
    break;
82
0
  case Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC:
83
0
    health_status.set_failed_active_degraded_check(
84
0
        host.healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC));
85
0
    break;
86
0
  case Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL:
87
0
    health_status.set_pending_dynamic_removal(
88
0
        host.healthFlagGet(Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL));
89
0
    break;
90
0
  case Upstream::Host::HealthFlag::PENDING_ACTIVE_HC:
91
0
    health_status.set_pending_active_hc(
92
0
        host.healthFlagGet(Upstream::Host::HealthFlag::PENDING_ACTIVE_HC));
93
0
    break;
94
0
  case Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL:
95
0
    health_status.set_excluded_via_immediate_hc_fail(
96
0
        host.healthFlagGet(Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL));
97
0
    break;
98
0
  case Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT:
99
0
    health_status.set_active_hc_timeout(
100
0
        host.healthFlagGet(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT));
101
0
    break;
102
0
  }
103
0
}
104
105
// TODO(efimki): Add support of text readouts stats.
106
0
void ClustersHandler::writeClustersAsJson(Buffer::Instance& response) {
107
0
  envoy::admin::v3::Clusters clusters;
108
  // TODO(mattklein123): Add ability to see warming clusters in admin output.
109
0
  auto all_clusters = server_.clusterManager().clusters();
110
0
  for (const auto& [name, cluster_ref] : all_clusters.active_clusters_) {
111
0
    UNREFERENCED_PARAMETER(name);
112
0
    const Upstream::Cluster& cluster = cluster_ref.get();
113
0
    Upstream::ClusterInfoConstSharedPtr cluster_info = cluster.info();
114
115
0
    envoy::admin::v3::ClusterStatus& cluster_status = *clusters.add_cluster_statuses();
116
0
    cluster_status.set_name(cluster_info->name());
117
0
    cluster_status.set_observability_name(cluster_info->observabilityName());
118
0
    if (const auto& name = cluster_info->edsServiceName(); !name.empty()) {
119
0
      cluster_status.set_eds_service_name(name);
120
0
    }
121
0
    addCircuitBreakerSettingsAsJson(
122
0
        envoy::config::core::v3::RoutingPriority::DEFAULT,
123
0
        cluster.info()->resourceManager(Upstream::ResourcePriority::Default), cluster_status);
124
0
    addCircuitBreakerSettingsAsJson(
125
0
        envoy::config::core::v3::RoutingPriority::HIGH,
126
0
        cluster.info()->resourceManager(Upstream::ResourcePriority::High), cluster_status);
127
128
0
    const Upstream::Outlier::Detector* outlier_detector = cluster.outlierDetector();
129
0
    if (outlier_detector != nullptr &&
130
0
        outlier_detector->successRateEjectionThreshold(
131
0
            Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin) > 0.0) {
132
0
      cluster_status.mutable_success_rate_ejection_threshold()->set_value(
133
0
          outlier_detector->successRateEjectionThreshold(
134
0
              Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin));
135
0
    }
136
0
    if (outlier_detector != nullptr &&
137
0
        outlier_detector->successRateEjectionThreshold(
138
0
            Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin) > 0.0) {
139
0
      cluster_status.mutable_local_origin_success_rate_ejection_threshold()->set_value(
140
0
          outlier_detector->successRateEjectionThreshold(
141
0
              Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin));
142
0
    }
143
144
0
    cluster_status.set_added_via_api(cluster_info->addedViaApi());
145
146
0
    for (auto& host_set : cluster.prioritySet().hostSetsPerPriority()) {
147
0
      for (auto& host : host_set->hosts()) {
148
0
        envoy::admin::v3::HostStatus& host_status = *cluster_status.add_host_statuses();
149
0
        Network::Utility::addressToProtobufAddress(*host->address(),
150
0
                                                   *host_status.mutable_address());
151
0
        host_status.set_hostname(host->hostname());
152
0
        host_status.mutable_locality()->MergeFrom(host->locality());
153
154
0
        for (const auto& [counter_name, counter] : host->counters()) {
155
0
          auto& metric = *host_status.add_stats();
156
0
          metric.set_name(std::string(counter_name));
157
0
          metric.set_value(counter.get().value());
158
0
          metric.set_type(envoy::admin::v3::SimpleMetric::COUNTER);
159
0
        }
160
161
0
        for (const auto& [gauge_name, gauge] : host->gauges()) {
162
0
          auto& metric = *host_status.add_stats();
163
0
          metric.set_name(std::string(gauge_name));
164
0
          metric.set_value(gauge.get().value());
165
0
          metric.set_type(envoy::admin::v3::SimpleMetric::GAUGE);
166
0
        }
167
168
0
        envoy::admin::v3::HostHealthStatus& health_status = *host_status.mutable_health_status();
169
170
// Invokes setHealthFlag for each health flag.
171
0
#define SET_HEALTH_FLAG(name, notused)                                                             \
172
0
  setHealthFlag(Upstream::Host::HealthFlag::name, *host, health_status);
173
0
        HEALTH_FLAG_ENUM_VALUES(SET_HEALTH_FLAG)
174
0
#undef SET_HEALTH_FLAG
175
176
0
        double success_rate = host->outlierDetector().successRate(
177
0
            Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin);
178
0
        if (success_rate >= 0.0) {
179
0
          host_status.mutable_success_rate()->set_value(success_rate);
180
0
        }
181
182
0
        host_status.set_weight(host->weight());
183
184
0
        host_status.set_priority(host->priority());
185
0
        success_rate = host->outlierDetector().successRate(
186
0
            Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin);
187
0
        if (success_rate >= 0.0) {
188
0
          host_status.mutable_local_origin_success_rate()->set_value(success_rate);
189
0
        }
190
0
      }
191
0
    }
192
0
  }
193
0
  response.add(MessageUtil::getJsonStringFromMessageOrError(clusters, true)); // pretty-print
194
0
}
195
196
// TODO(efimki): Add support of text readouts stats.
197
0
void ClustersHandler::writeClustersAsText(Buffer::Instance& response) {
198
  // TODO(mattklein123): Add ability to see warming clusters in admin output.
199
0
  auto all_clusters = server_.clusterManager().clusters();
200
0
  for (const auto& [name, cluster_ref] : all_clusters.active_clusters_) {
201
0
    UNREFERENCED_PARAMETER(name);
202
0
    const Upstream::Cluster& cluster = cluster_ref.get();
203
0
    const std::string& cluster_name = cluster.info()->name();
204
0
    response.add(fmt::format("{}::observability_name::{}\n", cluster_name,
205
0
                             cluster.info()->observabilityName()));
206
0
    addOutlierInfo(cluster_name, cluster.outlierDetector(), response);
207
208
0
    addCircuitBreakerSettingsAsText(
209
0
        cluster_name, "default",
210
0
        cluster.info()->resourceManager(Upstream::ResourcePriority::Default), response);
211
0
    addCircuitBreakerSettingsAsText(
212
0
        cluster_name, "high", cluster.info()->resourceManager(Upstream::ResourcePriority::High),
213
0
        response);
214
215
0
    response.add(
216
0
        fmt::format("{}::added_via_api::{}\n", cluster_name, cluster.info()->addedViaApi()));
217
0
    if (const auto& name = cluster.info()->edsServiceName(); !name.empty()) {
218
0
      response.add(fmt::format("{}::eds_service_name::{}\n", cluster_name, name));
219
0
    }
220
0
    for (auto& host_set : cluster.prioritySet().hostSetsPerPriority()) {
221
0
      for (auto& host : host_set->hosts()) {
222
0
        const std::string& host_address = host->address()->asString();
223
0
        std::map<absl::string_view, uint64_t> all_stats;
224
0
        for (const auto& [counter_name, counter] : host->counters()) {
225
0
          all_stats[counter_name] = counter.get().value();
226
0
        }
227
228
0
        for (const auto& [gauge_name, gauge] : host->gauges()) {
229
0
          all_stats[gauge_name] = gauge.get().value();
230
0
        }
231
232
0
        for (const auto& [stat_name, stat] : all_stats) {
233
0
          response.add(
234
0
              fmt::format("{}::{}::{}::{}\n", cluster_name, host_address, stat_name, stat));
235
0
        }
236
237
0
        response.add(
238
0
            fmt::format("{}::{}::hostname::{}\n", cluster_name, host_address, host->hostname()));
239
0
        response.add(fmt::format("{}::{}::health_flags::{}\n", cluster_name, host_address,
240
0
                                 Upstream::HostUtility::healthFlagsToString(*host)));
241
0
        response.add(
242
0
            fmt::format("{}::{}::weight::{}\n", cluster_name, host_address, host->weight()));
243
0
        response.add(fmt::format("{}::{}::region::{}\n", cluster_name, host_address,
244
0
                                 host->locality().region()));
245
0
        response.add(
246
0
            fmt::format("{}::{}::zone::{}\n", cluster_name, host_address, host->locality().zone()));
247
0
        response.add(fmt::format("{}::{}::sub_zone::{}\n", cluster_name, host_address,
248
0
                                 host->locality().sub_zone()));
249
0
        response.add(
250
0
            fmt::format("{}::{}::canary::{}\n", cluster_name, host_address, host->canary()));
251
0
        response.add(
252
0
            fmt::format("{}::{}::priority::{}\n", cluster_name, host_address, host->priority()));
253
0
        response.add(fmt::format(
254
0
            "{}::{}::success_rate::{}\n", cluster_name, host_address,
255
0
            host->outlierDetector().successRate(
256
0
                Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin)));
257
0
        response.add(fmt::format(
258
0
            "{}::{}::local_origin_success_rate::{}\n", cluster_name, host_address,
259
0
            host->outlierDetector().successRate(
260
0
                Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin)));
261
0
      }
262
0
    }
263
0
  }
264
0
}
265
266
void ClustersHandler::addOutlierInfo(const std::string& cluster_name,
267
                                     const Upstream::Outlier::Detector* outlier_detector,
268
0
                                     Buffer::Instance& response) {
269
0
  if (outlier_detector) {
270
0
    response.add(fmt::format(
271
0
        "{}::outlier::success_rate_average::{:g}\n", cluster_name,
272
0
        outlier_detector->successRateAverage(
273
0
            Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin)));
274
0
    response.add(fmt::format(
275
0
        "{}::outlier::success_rate_ejection_threshold::{:g}\n", cluster_name,
276
0
        outlier_detector->successRateEjectionThreshold(
277
0
            Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin)));
278
0
    response.add(fmt::format(
279
0
        "{}::outlier::local_origin_success_rate_average::{:g}\n", cluster_name,
280
0
        outlier_detector->successRateAverage(
281
0
            Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin)));
282
0
    response.add(fmt::format(
283
0
        "{}::outlier::local_origin_success_rate_ejection_threshold::{:g}\n", cluster_name,
284
0
        outlier_detector->successRateEjectionThreshold(
285
0
            Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin)));
286
0
  }
287
0
}
288
289
} // namespace Server
290
} // namespace Envoy