1
#include "source/common/upstream/host_utility.h"
2

            
3
#include <string>
4

            
5
#include "source/common/config/well_known_names.h"
6
#include "source/common/runtime/runtime_features.h"
7

            
8
namespace Envoy {
9
namespace Upstream {
10
namespace {
11

            
12
132
void setHealthFlag(Upstream::Host::HealthFlag flag, const Host& host, std::string& health_status) {
13
132
  switch (flag) {
14
12
  case Host::HealthFlag::FAILED_ACTIVE_HC: {
15
12
    if (host.healthFlagGet(Host::HealthFlag::FAILED_ACTIVE_HC)) {
16
5
      health_status += "/failed_active_hc";
17
5
    }
18
12
    break;
19
  }
20

            
21
12
  case Host::HealthFlag::FAILED_OUTLIER_CHECK: {
22
12
    if (host.healthFlagGet(Host::HealthFlag::FAILED_OUTLIER_CHECK)) {
23
6
      health_status += "/failed_outlier_check";
24
6
    }
25
12
    break;
26
  }
27

            
28
12
  case Host::HealthFlag::FAILED_EDS_HEALTH: {
29
12
    if (host.healthFlagGet(Host::HealthFlag::FAILED_EDS_HEALTH)) {
30
2
      health_status += "/failed_eds_health";
31
2
    }
32
12
    break;
33
  }
34

            
35
12
  case Host::HealthFlag::DEGRADED_ACTIVE_HC: {
36
12
    if (host.healthFlagGet(Host::HealthFlag::DEGRADED_ACTIVE_HC)) {
37
2
      health_status += "/degraded_active_hc";
38
2
    }
39
12
    break;
40
  }
41

            
42
12
  case Host::HealthFlag::DEGRADED_EDS_HEALTH: {
43
12
    if (host.healthFlagGet(Host::HealthFlag::DEGRADED_EDS_HEALTH)) {
44
2
      health_status += "/degraded_eds_health";
45
2
    }
46
12
    break;
47
  }
48

            
49
12
  case Host::HealthFlag::DEGRADED_OUTLIER_DETECTION: {
50
12
    if (host.healthFlagGet(Host::HealthFlag::DEGRADED_OUTLIER_DETECTION)) {
51
1
      health_status += "/degraded_outlier_detection";
52
1
    }
53
12
    break;
54
  }
55

            
56
12
  case Host::HealthFlag::PENDING_DYNAMIC_REMOVAL: {
57
12
    if (host.healthFlagGet(Host::HealthFlag::PENDING_DYNAMIC_REMOVAL)) {
58
2
      health_status += "/pending_dynamic_removal";
59
2
    }
60
12
    break;
61
  }
62

            
63
12
  case Host::HealthFlag::PENDING_ACTIVE_HC: {
64
12
    if (host.healthFlagGet(Host::HealthFlag::PENDING_ACTIVE_HC)) {
65
1
      health_status += "/pending_active_hc";
66
1
    }
67
12
    break;
68
  }
69

            
70
12
  case Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL: {
71
12
    if (host.healthFlagGet(Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL)) {
72
1
      health_status += "/excluded_via_immediate_hc_fail";
73
1
    }
74
12
    break;
75
  }
76

            
77
12
  case Host::HealthFlag::ACTIVE_HC_TIMEOUT: {
78
12
    if (host.healthFlagGet(Host::HealthFlag::ACTIVE_HC_TIMEOUT)) {
79
1
      health_status += "/active_hc_timeout";
80
1
    }
81
12
    break;
82
  }
83

            
84
12
  case Host::HealthFlag::EDS_STATUS_DRAINING: {
85
12
    if (host.healthFlagGet(Host::HealthFlag::EDS_STATUS_DRAINING)) {
86
3
      health_status += "/eds_status_draining";
87
3
    }
88
12
    break;
89
  }
90
132
  }
91
132
}
92

            
93
} // namespace
94

            
95
12
std::string HostUtility::healthFlagsToString(const Host& host) {
96
12
  std::string health_status;
97

            
98
  // Invokes setHealthFlag for each health flag.
99
12
#define SET_HEALTH_FLAG(name, notused)                                                             \
100
132
  setHealthFlag(Upstream::Host::HealthFlag::name, host, health_status);
101
132
  HEALTH_FLAG_ENUM_VALUES(SET_HEALTH_FLAG)
102
12
#undef SET_HEALTH_FLAG
103

            
104
12
  if (health_status.empty()) {
105
4
    return "healthy";
106
11
  } else {
107
8
    return health_status;
108
8
  }
109
12
}
110

            
111
HostUtility::HostStatusSet HostUtility::createOverrideHostStatus(
112
33898
    const envoy::config::cluster::v3::Cluster::CommonLbConfig& common_config) {
113
33898
  HostStatusSet override_host_status;
114

            
115
33898
  if (!common_config.has_override_host_status()) {
116
    // No override host status and [UNKNOWN, HEALTHY, DEGRADED] will be applied by default.
117
33893
    override_host_status.set(static_cast<uint32_t>(envoy::config::core::v3::HealthStatus::UNKNOWN));
118
33893
    override_host_status.set(static_cast<uint32_t>(envoy::config::core::v3::HealthStatus::HEALTHY));
119
33893
    override_host_status.set(
120
33893
        static_cast<uint32_t>(envoy::config::core::v3::HealthStatus::DEGRADED));
121
33893
    return override_host_status;
122
33893
  }
123

            
124
17
  for (auto single_status : common_config.override_host_status().statuses()) {
125
17
    switch (static_cast<envoy::config::core::v3::HealthStatus>(single_status)) {
126
      PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
127
3
    case envoy::config::core::v3::HealthStatus::UNKNOWN:
128
6
    case envoy::config::core::v3::HealthStatus::HEALTHY:
129
9
    case envoy::config::core::v3::HealthStatus::UNHEALTHY:
130
12
    case envoy::config::core::v3::HealthStatus::DRAINING:
131
15
    case envoy::config::core::v3::HealthStatus::TIMEOUT:
132
17
    case envoy::config::core::v3::HealthStatus::DEGRADED:
133
17
      override_host_status.set(static_cast<uint32_t>(single_status));
134
17
      break;
135
17
    }
136
17
  }
137
5
  return override_host_status;
138
5
}
139

            
140
std::pair<HostConstSharedPtr, bool> HostUtility::selectOverrideHost(const HostMap* host_map,
141
                                                                    HostStatusSet status,
142
50228
                                                                    LoadBalancerContext* context) {
143
50228
  if (context == nullptr) {
144
199
    return {nullptr, false};
145
199
  }
146

            
147
50029
  auto override_host = context->overrideHostToSelect();
148
50029
  if (!override_host.has_value()) {
149
49958
    return {nullptr, false};
150
49958
  }
151

            
152
71
  const bool strict_mode = override_host.value().second;
153

            
154
71
  if (host_map == nullptr) {
155
2
    return {nullptr, strict_mode};
156
2
  }
157

            
158
69
  auto host_iter = host_map->find(override_host.value().first);
159

            
160
  // The override host cannot be found in the host map.
161
69
  if (host_iter == host_map->end()) {
162
10
    return {nullptr, strict_mode};
163
10
  }
164

            
165
59
  HostConstSharedPtr host = host_iter->second;
166
59
  ASSERT(host != nullptr);
167

            
168
59
  if (status[static_cast<uint32_t>(host->healthStatus())]) {
169
39
    return {host, strict_mode};
170
39
  }
171
20
  return {nullptr, strict_mode};
172
59
}
173

            
174
void HostUtility::forEachHostMetric(
175
    const ClusterManager& cluster_manager,
176
    const std::function<void(Stats::PrimitiveCounterSnapshot&& metric)>& counter_cb,
177
12317
    const std::function<void(Stats::PrimitiveGaugeSnapshot&& metric)>& gauge_cb) {
178
18857
  for (const auto& [unused_name, cluster_ref] : cluster_manager.clusters().active_clusters_) {
179
18428
    Upstream::ClusterInfoConstSharedPtr cluster_info = cluster_ref.get().info();
180
18428
    if (cluster_info->perEndpointStatsEnabled()) {
181
26
      const std::string cluster_name =
182
26
          Stats::Utility::sanitizeStatsName(cluster_info->observabilityName());
183

            
184
26
      const Stats::TagVector& fixed_tags = cluster_info->statsScope().store().fixedTags();
185

            
186
28
      for (auto& host_set : cluster_ref.get().prioritySet().hostSetsPerPriority()) {
187
33
        for (auto& host : host_set->hosts()) {
188

            
189
33
          Stats::TagVector tags;
190
33
          tags.reserve(fixed_tags.size() + 3);
191
33
          tags.insert(tags.end(), fixed_tags.begin(), fixed_tags.end());
192
33
          tags.emplace_back(Stats::Tag{Envoy::Config::TagNames::get().CLUSTER_NAME, cluster_name});
193
33
          tags.emplace_back(Stats::Tag{"envoy.endpoint_address", host->address()->asString()});
194

            
195
33
          const auto& hostname = host->hostname();
196
33
          if (!hostname.empty()) {
197
1
            tags.push_back({"envoy.endpoint_hostname", hostname});
198
1
          }
199

            
200
33
          auto set_metric_metadata = [&](absl::string_view metric_name,
201
147
                                         Stats::PrimitiveMetricMetadata& metric) {
202
147
            metric.setName(
203
147
                absl::StrCat("cluster.", cluster_name, ".endpoint.",
204
147
                             Stats::Utility::sanitizeStatsName(host->address()->asStringView()),
205
147
                             ".", metric_name));
206
147
            metric.setTagExtractedName(absl::StrCat("cluster.endpoint.", metric_name));
207
147
            metric.setTags(tags);
208

            
209
            // Validate that all components were sanitized.
210
147
            ASSERT(metric.name() == Stats::Utility::sanitizeStatsName(metric.name()));
211
147
            ASSERT(metric.tagExtractedName() ==
212
147
                   Stats::Utility::sanitizeStatsName(metric.tagExtractedName()));
213
147
          };
214

            
215
60
          for (auto& [metric_name, primitive] : host->counters()) {
216
57
            Stats::PrimitiveCounterSnapshot metric(primitive.get());
217
57
            set_metric_metadata(metric_name, metric);
218

            
219
57
            counter_cb(std::move(metric));
220
57
          }
221

            
222
33
          auto gauges = host->gauges();
223

            
224
          // Add synthetic "healthy" gauge.
225
33
          Stats::PrimitiveGauge healthy_gauge;
226
33
          healthy_gauge.set((host->coarseHealth() == Host::Health::Healthy) ? 1 : 0);
227
33
          gauges.emplace_back(absl::string_view("healthy"), healthy_gauge);
228

            
229
90
          for (auto& [metric_name, primitive] : gauges) {
230
90
            Stats::PrimitiveGaugeSnapshot metric(primitive.get());
231
90
            set_metric_metadata(metric_name, metric);
232
90
            gauge_cb(std::move(metric));
233
90
          }
234
33
        }
235
28
      }
236
26
    }
237
18428
  }
238
12317
}
239

            
240
} // namespace Upstream
241
} // namespace Envoy