1 // Copyright 2017 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15
16 #include "test/cpp/end2end/xds/xds_utils.h"
17
18 #include <functional>
19 #include <map>
20 #include <memory>
21 #include <set>
22 #include <string>
23 #include <thread>
24 #include <vector>
25
26 #include "absl/memory/memory.h"
27 #include "absl/strings/str_cat.h"
28 #include "absl/strings/str_format.h"
29 #include "absl/strings/str_join.h"
30 #include "absl/strings/str_replace.h"
31 #include "absl/strings/string_view.h"
32 #include "absl/types/optional.h"
33
34 #include <grpcpp/security/tls_certificate_provider.h>
35
36 #include "src/core/ext/filters/http/server/http_server_filter.h"
37 #include "src/core/ext/xds/xds_channel_args.h"
38 #include "src/core/ext/xds/xds_client_grpc.h"
39 #include "src/core/lib/gpr/tmpfile.h"
40 #include "src/core/lib/gprpp/env.h"
41 #include "src/core/lib/surface/server.h"
42 #include "src/cpp/client/secure_credentials.h"
43 #include "src/proto/grpc/testing/xds/v3/router.grpc.pb.h"
44 #include "test/core/util/resolve_localhost_ip46.h"
45
46 namespace grpc {
47 namespace testing {
48
49 using ::envoy::config::cluster::v3::Cluster;
50 using ::envoy::config::core::v3::HealthStatus;
51 using ::envoy::config::endpoint::v3::ClusterLoadAssignment;
52 using ::envoy::config::listener::v3::Listener;
53 using ::envoy::config::route::v3::RouteConfiguration;
54 using ::envoy::extensions::filters::network::http_connection_manager::v3::
55 HttpConnectionManager;
56
57 //
58 // XdsBootstrapBuilder
59 //
60
Build()61 std::string XdsBootstrapBuilder::Build() {
62 std::vector<std::string> fields;
63 fields.push_back(MakeXdsServersText(servers_));
64 if (!client_default_listener_resource_name_template_.empty()) {
65 fields.push_back(
66 absl::StrCat(" \"client_default_listener_resource_name_template\": \"",
67 client_default_listener_resource_name_template_, "\""));
68 }
69 fields.push_back(MakeNodeText());
70 if (!server_listener_resource_name_template_.empty()) {
71 fields.push_back(
72 absl::StrCat(" \"server_listener_resource_name_template\": \"",
73 server_listener_resource_name_template_, "\""));
74 }
75 fields.push_back(MakeCertificateProviderText());
76 fields.push_back(MakeAuthorityText());
77 return absl::StrCat("{", absl::StrJoin(fields, ",\n"), "}");
78 }
79
MakeXdsServersText(absl::Span<const std::string> server_uris)80 std::string XdsBootstrapBuilder::MakeXdsServersText(
81 absl::Span<const std::string> server_uris) {
82 constexpr char kXdsServerTemplate[] =
83 " {\n"
84 " \"server_uri\": \"<SERVER_URI>\",\n"
85 " \"channel_creds\": [\n"
86 " {\n"
87 " \"type\": \"<SERVER_CREDS_TYPE>\"\n"
88 " }\n"
89 " ],\n"
90 " \"server_features\": [<SERVER_FEATURES>]\n"
91 " }";
92 std::vector<std::string> server_features;
93 if (ignore_resource_deletion_) {
94 server_features.push_back("\"ignore_resource_deletion\"");
95 }
96 std::vector<std::string> servers;
97 for (absl::string_view server_uri : server_uris) {
98 servers.emplace_back(absl::StrReplaceAll(
99 kXdsServerTemplate,
100 {{"<SERVER_URI>", server_uri},
101 {"<SERVER_CREDS_TYPE>", xds_channel_creds_type_},
102 {"<SERVER_FEATURES>", absl::StrJoin(server_features, ", ")}}));
103 }
104 return absl::StrCat(" \"xds_servers\": [\n",
105 absl::StrJoin(servers, ",\n"), "\n ]");
106 }
107
MakeNodeText()108 std::string XdsBootstrapBuilder::MakeNodeText() {
109 constexpr char kXdsNode[] =
110 " \"node\": {\n"
111 " \"id\": \"xds_end2end_test\",\n"
112 " \"cluster\": \"test\",\n"
113 " \"metadata\": {\n"
114 " \"foo\": \"bar\"\n"
115 " },\n"
116 " \"locality\": {\n"
117 " \"region\": \"corp\",\n"
118 " \"zone\": \"svl\",\n"
119 " \"sub_zone\": \"mp3\"\n"
120 " }\n"
121 " }";
122 return kXdsNode;
123 }
124
MakeCertificateProviderText()125 std::string XdsBootstrapBuilder::MakeCertificateProviderText() {
126 std::vector<std::string> entries;
127 for (const auto& p : plugins_) {
128 const std::string& key = p.first;
129 const PluginInfo& plugin_info = p.second;
130 std::vector<std::string> fields;
131 fields.push_back(absl::StrFormat(" \"%s\": {", key));
132 if (!plugin_info.plugin_config.empty()) {
133 fields.push_back(
134 absl::StrFormat(" \"plugin_name\": \"%s\",", plugin_info.name));
135 fields.push_back(absl::StrCat(" \"config\": {\n",
136 plugin_info.plugin_config, "\n }"));
137 } else {
138 fields.push_back(
139 absl::StrFormat(" \"plugin_name\": \"%s\"", plugin_info.name));
140 }
141 fields.push_back(" }");
142 entries.push_back(absl::StrJoin(fields, "\n"));
143 }
144 return absl::StrCat(" \"certificate_providers\": {\n",
145 absl::StrJoin(entries, ",\n"), " \n}");
146 }
147
MakeAuthorityText()148 std::string XdsBootstrapBuilder::MakeAuthorityText() {
149 std::vector<std::string> entries;
150 for (const auto& p : authorities_) {
151 const std::string& name = p.first;
152 const AuthorityInfo& authority_info = p.second;
153 std::vector<std::string> fields = {
154 MakeXdsServersText(authority_info.servers)};
155 if (!authority_info.client_listener_resource_name_template.empty()) {
156 fields.push_back(absl::StrCat(
157 "\"client_listener_resource_name_template\": \"",
158 authority_info.client_listener_resource_name_template, "\""));
159 }
160 entries.push_back(absl::StrCat(absl::StrFormat("\"%s\": {\n ", name),
161 absl::StrJoin(fields, ",\n"), "\n}"));
162 }
163 return absl::StrCat("\"authorities\": {\n", absl::StrJoin(entries, ",\n"),
164 "\n}");
165 }
166
167 //
168 // XdsResourceUtils::ClientHcmAccessor
169 //
170
Unpack(const Listener & listener) const171 HttpConnectionManager XdsResourceUtils::ClientHcmAccessor::Unpack(
172 const Listener& listener) const {
173 HttpConnectionManager http_connection_manager;
174 listener.api_listener().api_listener().UnpackTo(&http_connection_manager);
175 return http_connection_manager;
176 }
177
Pack(const HttpConnectionManager & hcm,Listener * listener) const178 void XdsResourceUtils::ClientHcmAccessor::Pack(const HttpConnectionManager& hcm,
179 Listener* listener) const {
180 auto* api_listener = listener->mutable_api_listener()->mutable_api_listener();
181 api_listener->PackFrom(hcm);
182 }
183
184 //
185 // XdsResourceUtils::ServerHcmAccessor
186 //
187
Unpack(const Listener & listener) const188 HttpConnectionManager XdsResourceUtils::ServerHcmAccessor::Unpack(
189 const Listener& listener) const {
190 HttpConnectionManager http_connection_manager;
191 listener.default_filter_chain().filters().at(0).typed_config().UnpackTo(
192 &http_connection_manager);
193 return http_connection_manager;
194 }
195
Pack(const HttpConnectionManager & hcm,Listener * listener) const196 void XdsResourceUtils::ServerHcmAccessor::Pack(const HttpConnectionManager& hcm,
197 Listener* listener) const {
198 auto* filters = listener->mutable_default_filter_chain()->mutable_filters();
199 if (filters->empty()) filters->Add();
200 filters->at(0).mutable_typed_config()->PackFrom(hcm);
201 }
202
203 //
204 // XdsResourceUtils
205 //
206
207 const char XdsResourceUtils::kDefaultLocalityRegion[] =
208 "xds_default_locality_region";
209 const char XdsResourceUtils::kDefaultLocalityZone[] =
210 "xds_default_locality_zone";
211
212 const char XdsResourceUtils::kServerName[] = "server.example.com";
213 const char XdsResourceUtils::kDefaultRouteConfigurationName[] =
214 "route_config_name";
215 const char XdsResourceUtils::kDefaultClusterName[] = "cluster_name";
216 const char XdsResourceUtils::kDefaultEdsServiceName[] = "eds_service_name";
217 const char XdsResourceUtils::kDefaultServerRouteConfigurationName[] =
218 "default_server_route_config_name";
219
DefaultListener()220 Listener XdsResourceUtils::DefaultListener() {
221 Listener listener;
222 listener.set_name(kServerName);
223 ClientHcmAccessor().Pack(DefaultHcm(), &listener);
224 return listener;
225 }
226
DefaultRouteConfig()227 RouteConfiguration XdsResourceUtils::DefaultRouteConfig() {
228 RouteConfiguration route_config;
229 route_config.set_name(kDefaultRouteConfigurationName);
230 auto* virtual_host = route_config.add_virtual_hosts();
231 virtual_host->add_domains("*");
232 auto* route = virtual_host->add_routes();
233 route->mutable_match()->set_prefix("");
234 route->mutable_route()->set_cluster(kDefaultClusterName);
235 return route_config;
236 }
237
DefaultCluster()238 Cluster XdsResourceUtils::DefaultCluster() {
239 Cluster cluster;
240 cluster.set_name(kDefaultClusterName);
241 cluster.set_type(Cluster::EDS);
242 auto* eds_config = cluster.mutable_eds_cluster_config();
243 eds_config->mutable_eds_config()->mutable_self();
244 eds_config->set_service_name(kDefaultEdsServiceName);
245 cluster.set_lb_policy(Cluster::ROUND_ROBIN);
246 return cluster;
247 }
248
DefaultServerListener()249 Listener XdsResourceUtils::DefaultServerListener() {
250 Listener listener;
251 listener.mutable_address()->mutable_socket_address()->set_address(
252 grpc_core::LocalIp());
253 ServerHcmAccessor().Pack(DefaultHcm(), &listener);
254 return listener;
255 }
256
DefaultServerRouteConfig()257 RouteConfiguration XdsResourceUtils::DefaultServerRouteConfig() {
258 RouteConfiguration route_config;
259 route_config.set_name(kDefaultServerRouteConfigurationName);
260 auto* virtual_host = route_config.add_virtual_hosts();
261 virtual_host->add_domains("*");
262 auto* route = virtual_host->add_routes();
263 route->mutable_match()->set_prefix("");
264 route->mutable_non_forwarding_action();
265 return route_config;
266 }
267
DefaultHcm()268 HttpConnectionManager XdsResourceUtils::DefaultHcm() {
269 HttpConnectionManager http_connection_manager;
270 auto* filter = http_connection_manager.add_http_filters();
271 filter->set_name("router");
272 filter->mutable_typed_config()->PackFrom(
273 envoy::extensions::filters::http::router::v3::Router());
274 return http_connection_manager;
275 }
276
GetServerListenerName(int port)277 std::string XdsResourceUtils::GetServerListenerName(int port) {
278 return absl::StrCat("grpc/server?xds.resource.listening_address=",
279 grpc_core::LocalIp(), ":", port);
280 }
281
PopulateServerListenerNameAndPort(const Listener & listener_template,int port)282 Listener XdsResourceUtils::PopulateServerListenerNameAndPort(
283 const Listener& listener_template, int port) {
284 Listener listener = listener_template;
285 listener.set_name(GetServerListenerName(port));
286 listener.mutable_address()->mutable_socket_address()->set_port_value(port);
287 return listener;
288 }
289
SetListenerAndRouteConfiguration(AdsServiceImpl * ads_service,Listener listener,const RouteConfiguration & route_config,bool use_rds,const HcmAccessor & hcm_accessor)290 void XdsResourceUtils::SetListenerAndRouteConfiguration(
291 AdsServiceImpl* ads_service, Listener listener,
292 const RouteConfiguration& route_config, bool use_rds,
293 const HcmAccessor& hcm_accessor) {
294 HttpConnectionManager http_connection_manager = hcm_accessor.Unpack(listener);
295 if (use_rds) {
296 auto* rds = http_connection_manager.mutable_rds();
297 rds->set_route_config_name(route_config.name());
298 rds->mutable_config_source()->mutable_self();
299 ads_service->SetRdsResource(route_config);
300 } else {
301 *http_connection_manager.mutable_route_config() = route_config;
302 }
303 hcm_accessor.Pack(http_connection_manager, &listener);
304 ads_service->SetLdsResource(listener);
305 }
306
SetRouteConfiguration(AdsServiceImpl * ads_service,const RouteConfiguration & route_config,bool use_rds,const Listener * listener_to_copy)307 void XdsResourceUtils::SetRouteConfiguration(
308 AdsServiceImpl* ads_service, const RouteConfiguration& route_config,
309 bool use_rds, const Listener* listener_to_copy) {
310 if (use_rds) {
311 ads_service->SetRdsResource(route_config);
312 } else {
313 Listener listener(listener_to_copy == nullptr ? DefaultListener()
314 : *listener_to_copy);
315 HttpConnectionManager http_connection_manager =
316 ClientHcmAccessor().Unpack(listener);
317 *(http_connection_manager.mutable_route_config()) = route_config;
318 ClientHcmAccessor().Pack(http_connection_manager, &listener);
319 ads_service->SetLdsResource(listener);
320 }
321 }
322
LocalityNameString(absl::string_view sub_zone)323 std::string XdsResourceUtils::LocalityNameString(absl::string_view sub_zone) {
324 return absl::StrFormat("{region=\"%s\", zone=\"%s\", sub_zone=\"%s\"}",
325 kDefaultLocalityRegion, kDefaultLocalityZone,
326 sub_zone);
327 }
328
BuildEdsResource(const EdsResourceArgs & args,absl::string_view eds_service_name)329 ClusterLoadAssignment XdsResourceUtils::BuildEdsResource(
330 const EdsResourceArgs& args, absl::string_view eds_service_name) {
331 ClusterLoadAssignment assignment;
332 assignment.set_cluster_name(eds_service_name);
333 for (const auto& locality : args.locality_list) {
334 auto* endpoints = assignment.add_endpoints();
335 endpoints->mutable_load_balancing_weight()->set_value(locality.lb_weight);
336 endpoints->set_priority(locality.priority);
337 endpoints->mutable_locality()->set_region(kDefaultLocalityRegion);
338 endpoints->mutable_locality()->set_zone(kDefaultLocalityZone);
339 endpoints->mutable_locality()->set_sub_zone(locality.sub_zone);
340 for (size_t i = 0; i < locality.endpoints.size(); ++i) {
341 const auto& endpoint = locality.endpoints[i];
342 auto* lb_endpoints = endpoints->add_lb_endpoints();
343 if (locality.endpoints.size() > i &&
344 locality.endpoints[i].health_status != HealthStatus::UNKNOWN) {
345 lb_endpoints->set_health_status(endpoint.health_status);
346 }
347 if (locality.endpoints.size() > i && endpoint.lb_weight >= 1) {
348 lb_endpoints->mutable_load_balancing_weight()->set_value(
349 endpoint.lb_weight);
350 }
351 auto* endpoint_proto = lb_endpoints->mutable_endpoint();
352 auto* socket_address =
353 endpoint_proto->mutable_address()->mutable_socket_address();
354 socket_address->set_address(grpc_core::LocalIp());
355 socket_address->set_port_value(endpoint.port);
356 for (int port : endpoint.additional_ports) {
357 socket_address = endpoint_proto->add_additional_addresses()
358 ->mutable_address()
359 ->mutable_socket_address();
360 socket_address->set_address(grpc_core::LocalIp());
361 socket_address->set_port_value(port);
362 }
363 }
364 }
365 if (!args.drop_categories.empty()) {
366 auto* policy = assignment.mutable_policy();
367 for (const auto& p : args.drop_categories) {
368 const std::string& name = p.first;
369 const uint32_t parts_per_million = p.second;
370 auto* drop_overload = policy->add_drop_overloads();
371 drop_overload->set_category(name);
372 auto* drop_percentage = drop_overload->mutable_drop_percentage();
373 drop_percentage->set_numerator(parts_per_million);
374 drop_percentage->set_denominator(args.drop_denominator);
375 }
376 }
377 return assignment;
378 }
379
380 } // namespace testing
381 } // namespace grpc
382