1 //
2 //
3 // Copyright 2023 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include "src/cpp/ext/csm/metadata_exchange.h"
20
21 #include <grpcpp/ext/otel_plugin.h>
22 #include <grpcpp/grpcpp.h>
23
24 #include "absl/functional/any_invocable.h"
25 #include "gmock/gmock.h"
26 #include "google/cloud/opentelemetry/resource_detector.h"
27 #include "gtest/gtest.h"
28 #include "opentelemetry/metrics/provider.h"
29 #include "opentelemetry/sdk/metrics/meter_provider.h"
30 #include "opentelemetry/sdk/metrics/metric_reader.h"
31 #include "src/core/config/core_configuration.h"
32 #include "src/core/telemetry/call_tracer.h"
33 #include "src/core/util/env.h"
34 #include "src/core/util/tmpfile.h"
35 #include "src/cpp/ext/csm/csm_observability.h"
36 #include "src/cpp/ext/otel/otel_plugin.h"
37 #include "test/core/test_util/test_config.h"
38 #include "test/cpp/end2end/test_service_impl.h"
39 #include "test/cpp/ext/otel/otel_test_library.h"
40
41 namespace grpc {
42 namespace testing {
43 namespace {
44
45 using OptionalLabelKey =
46 grpc_core::ClientCallTracer::CallAttemptTracer::OptionalLabelKey;
47 using ::testing::ElementsAre;
48 using ::testing::Pair;
49
TestGkeResource()50 opentelemetry::sdk::resource::Resource TestGkeResource() {
51 opentelemetry::sdk::common::AttributeMap attributes;
52 attributes.SetAttribute("cloud.platform", "gcp_kubernetes_engine");
53 attributes.SetAttribute("k8s.pod.name", "pod");
54 attributes.SetAttribute("k8s.container.name", "container");
55 attributes.SetAttribute("k8s.namespace.name", "namespace");
56 attributes.SetAttribute("k8s.cluster.name", "cluster");
57 attributes.SetAttribute("cloud.region", "region");
58 attributes.SetAttribute("cloud.account.id", "id");
59 return opentelemetry::sdk::resource::Resource::Create(attributes);
60 }
61
TestGceResource()62 opentelemetry::sdk::resource::Resource TestGceResource() {
63 opentelemetry::sdk::common::AttributeMap attributes;
64 attributes.SetAttribute("cloud.platform", "gcp_compute_engine");
65 attributes.SetAttribute("cloud.availability_zone", "zone");
66 attributes.SetAttribute("cloud.account.id", "id");
67 return opentelemetry::sdk::resource::Resource::Create(attributes);
68 }
69
TestUnknownResource()70 opentelemetry::sdk::resource::Resource TestUnknownResource() {
71 opentelemetry::sdk::common::AttributeMap attributes;
72 attributes.SetAttribute("cloud.platform", "random");
73 return opentelemetry::sdk::resource::Resource::Create(attributes);
74 }
75
76 class TestScenario {
77 public:
78 enum class ResourceType : std::uint8_t { kGke, kGce, kUnknown };
79
TestScenario(ResourceType type)80 explicit TestScenario(ResourceType type) : type_(type) {}
81
GetTestResource() const82 opentelemetry::sdk::resource::Resource GetTestResource() const {
83 switch (type_) {
84 case ResourceType::kGke:
85 return TestGkeResource();
86 case ResourceType::kGce:
87 return TestGceResource();
88 case ResourceType::kUnknown:
89 return TestUnknownResource();
90 }
91 }
92
Name(const::testing::TestParamInfo<TestScenario> & info)93 static std::string Name(const ::testing::TestParamInfo<TestScenario>& info) {
94 std::string ret_val;
95 switch (info.param.type_) {
96 case ResourceType::kGke:
97 ret_val += "Gke";
98 break;
99 case ResourceType::kGce:
100 ret_val += "Gce";
101 break;
102 case ResourceType::kUnknown:
103 ret_val += "Unknown";
104 break;
105 }
106 return ret_val;
107 }
108
type() const109 ResourceType type() const { return type_; }
110
111 private:
112 ResourceType type_;
113 };
114
115 // A PluginOption that injects `ServiceMeshLabelsInjector`. (This is different
116 // from CsmOpenTelemetryPluginOption since it does not restrict itself to just
117 // CSM channels and servers.)
118 class MeshLabelsPluginOption
119 : public grpc::internal::InternalOpenTelemetryPluginOption {
120 public:
MeshLabelsPluginOption(const opentelemetry::sdk::common::AttributeMap & map)121 explicit MeshLabelsPluginOption(
122 const opentelemetry::sdk::common::AttributeMap& map)
123 : labels_injector_(
124 std::make_unique<grpc::internal::ServiceMeshLabelsInjector>(map)) {}
125
IsActiveOnClientChannel(absl::string_view) const126 bool IsActiveOnClientChannel(absl::string_view /*target*/) const override {
127 return true;
128 }
129
IsActiveOnServer(const grpc_core::ChannelArgs &) const130 bool IsActiveOnServer(const grpc_core::ChannelArgs& /*args*/) const override {
131 return true;
132 }
133
labels_injector() const134 const grpc::internal::LabelsInjector* labels_injector() const override {
135 return labels_injector_.get();
136 }
137
138 private:
139 std::unique_ptr<grpc::internal::ServiceMeshLabelsInjector> labels_injector_;
140 };
141
142 class MetadataExchangeTest
143 : public OpenTelemetryPluginEnd2EndTest,
144 public ::testing::WithParamInterface<TestScenario> {
145 protected:
Init(Options options,bool enable_client_side_injector=true)146 void Init(Options options, bool enable_client_side_injector = true) {
147 OpenTelemetryPluginEnd2EndTest::Init(std::move(
148 options
149 .add_plugin_option(std::make_unique<MeshLabelsPluginOption>(
150 GetParam().GetTestResource().GetAttributes()))
151 .set_channel_scope_filter(
152 [enable_client_side_injector](
153 const OpenTelemetryPluginBuilder::ChannelScope& /*scope*/) {
154 return enable_client_side_injector;
155 })));
156 }
157
VerifyServiceMeshAttributes(const std::map<std::string,opentelemetry::sdk::common::OwnedAttributeValue> & attributes,bool is_client)158 void VerifyServiceMeshAttributes(
159 const std::map<std::string,
160 opentelemetry::sdk::common::OwnedAttributeValue>&
161 attributes,
162 bool is_client) {
163 EXPECT_EQ(
164 absl::get<std::string>(attributes.at("csm.workload_canonical_service")),
165 "canonical_service");
166 EXPECT_EQ(absl::get<std::string>(attributes.at("csm.mesh_id")), "mesh-id");
167 EXPECT_EQ(absl::get<std::string>(
168 attributes.at("csm.remote_workload_canonical_service")),
169 "canonical_service");
170 if (is_client) {
171 EXPECT_EQ(absl::get<std::string>(attributes.at("csm.service_name")),
172 "unknown");
173 EXPECT_EQ(
174 absl::get<std::string>(attributes.at("csm.service_namespace_name")),
175 "unknown");
176 } else {
177 // The CSM optional labels should not be present in server metrics.
178 EXPECT_THAT(attributes, ::testing::Not(::testing::Contains(
179 ::testing::Key("csm.service_name"))));
180 EXPECT_THAT(attributes, ::testing::Not(::testing::Contains(::testing::Key(
181 "csm.service_namespace_name"))));
182 }
183 switch (GetParam().type()) {
184 case TestScenario::ResourceType::kGke:
185 EXPECT_EQ(
186 absl::get<std::string>(attributes.at("csm.remote_workload_type")),
187 "gcp_kubernetes_engine");
188 EXPECT_EQ(
189 absl::get<std::string>(attributes.at("csm.remote_workload_name")),
190 "workload");
191 EXPECT_EQ(absl::get<std::string>(
192 attributes.at("csm.remote_workload_namespace_name")),
193 "namespace");
194 EXPECT_EQ(absl::get<std::string>(
195 attributes.at("csm.remote_workload_cluster_name")),
196 "cluster");
197 EXPECT_EQ(absl::get<std::string>(
198 attributes.at("csm.remote_workload_location")),
199 "region");
200 EXPECT_EQ(absl::get<std::string>(
201 attributes.at("csm.remote_workload_project_id")),
202 "id");
203 break;
204 case TestScenario::ResourceType::kGce:
205 EXPECT_EQ(
206 absl::get<std::string>(attributes.at("csm.remote_workload_type")),
207 "gcp_compute_engine");
208 EXPECT_EQ(
209 absl::get<std::string>(attributes.at("csm.remote_workload_name")),
210 "workload");
211 EXPECT_EQ(absl::get<std::string>(
212 attributes.at("csm.remote_workload_location")),
213 "zone");
214 EXPECT_EQ(absl::get<std::string>(
215 attributes.at("csm.remote_workload_project_id")),
216 "id");
217 break;
218 case TestScenario::ResourceType::kUnknown:
219 EXPECT_EQ(
220 absl::get<std::string>(attributes.at("csm.remote_workload_type")),
221 "random");
222 break;
223 }
224 }
225
VerifyNoServiceMeshAttributes(const std::map<std::string,opentelemetry::sdk::common::OwnedAttributeValue> & attributes)226 void VerifyNoServiceMeshAttributes(
227 const std::map<std::string,
228 opentelemetry::sdk::common::OwnedAttributeValue>&
229 attributes) {
230 EXPECT_EQ(attributes.find("csm.remote_workload_type"), attributes.end());
231 }
232 };
233
234 // Verify that grpc.client.attempt.started does not get service mesh attributes
TEST_P(MetadataExchangeTest,ClientAttemptStarted)235 TEST_P(MetadataExchangeTest, ClientAttemptStarted) {
236 Init(std::move(
237 Options().set_metric_names({grpc::OpenTelemetryPluginBuilder::
238 kClientAttemptStartedInstrumentName})));
239 SendRPC();
240 const char* kMetricName = "grpc.client.attempt.started";
241 auto data = ReadCurrentMetricsData(
242 [&](const absl::flat_hash_map<
243 std::string,
244 std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
245 data) { return !data.contains(kMetricName); });
246 ASSERT_EQ(data[kMetricName].size(), 1);
247 auto point_data = absl::get_if<opentelemetry::sdk::metrics::SumPointData>(
248 &data[kMetricName][0].point_data);
249 ASSERT_NE(point_data, nullptr);
250 auto client_started_value = absl::get_if<int64_t>(&point_data->value_);
251 ASSERT_NE(client_started_value, nullptr);
252 EXPECT_EQ(*client_started_value, 1);
253 const auto& attributes = data[kMetricName][0].attributes.GetAttributes();
254 EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.method")), kMethodName);
255 EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.target")),
256 canonical_server_address_);
257 VerifyNoServiceMeshAttributes(attributes);
258 }
259
TEST_P(MetadataExchangeTest,ClientAttemptDuration)260 TEST_P(MetadataExchangeTest, ClientAttemptDuration) {
261 Init(std::move(
262 Options().set_metric_names({grpc::OpenTelemetryPluginBuilder::
263 kClientAttemptDurationInstrumentName})));
264 SendRPC();
265 const char* kMetricName = "grpc.client.attempt.duration";
266 auto data = ReadCurrentMetricsData(
267 [&](const absl::flat_hash_map<
268 std::string,
269 std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
270 data) { return !data.contains(kMetricName); });
271 ASSERT_EQ(data[kMetricName].size(), 1);
272 auto point_data =
273 absl::get_if<opentelemetry::sdk::metrics::HistogramPointData>(
274 &data[kMetricName][0].point_data);
275 ASSERT_NE(point_data, nullptr);
276 ASSERT_EQ(point_data->count_, 1);
277 const auto& attributes = data[kMetricName][0].attributes.GetAttributes();
278 EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.method")), kMethodName);
279 EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.target")),
280 canonical_server_address_);
281 EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.status")), "OK");
282 VerifyServiceMeshAttributes(attributes, /*is_client=*/true);
283 }
284
285 // Verify that grpc.server.call.started does not get service mesh attributes
TEST_P(MetadataExchangeTest,ServerCallStarted)286 TEST_P(MetadataExchangeTest, ServerCallStarted) {
287 Init(std::move(Options().set_metric_names(
288 {grpc::OpenTelemetryPluginBuilder::kServerCallStartedInstrumentName})));
289 SendRPC();
290 const char* kMetricName = "grpc.server.call.started";
291 auto data = ReadCurrentMetricsData(
292 [&](const absl::flat_hash_map<
293 std::string,
294 std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
295 data) { return !data.contains(kMetricName); });
296 ASSERT_EQ(data[kMetricName].size(), 1);
297 auto point_data = absl::get_if<opentelemetry::sdk::metrics::SumPointData>(
298 &data[kMetricName][0].point_data);
299 ASSERT_NE(point_data, nullptr);
300 ASSERT_EQ(absl::get<int64_t>(point_data->value_), 1);
301 const auto& attributes = data[kMetricName][0].attributes.GetAttributes();
302 EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.method")), kMethodName);
303 VerifyNoServiceMeshAttributes(attributes);
304 }
305
TEST_P(MetadataExchangeTest,ServerCallDuration)306 TEST_P(MetadataExchangeTest, ServerCallDuration) {
307 Init(std::move(Options().set_metric_names(
308 {grpc::OpenTelemetryPluginBuilder::kServerCallDurationInstrumentName})));
309 SendRPC();
310 const char* kMetricName = "grpc.server.call.duration";
311 auto data = ReadCurrentMetricsData(
312 [&](const absl::flat_hash_map<
313 std::string,
314 std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
315 data) { return !data.contains(kMetricName); });
316 ASSERT_EQ(data[kMetricName].size(), 1);
317 auto point_data =
318 absl::get_if<opentelemetry::sdk::metrics::HistogramPointData>(
319 &data[kMetricName][0].point_data);
320 ASSERT_NE(point_data, nullptr);
321 ASSERT_EQ(point_data->count_, 1);
322 const auto& attributes = data[kMetricName][0].attributes.GetAttributes();
323 EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.method")), kMethodName);
324 EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.status")), "OK");
325 VerifyServiceMeshAttributes(attributes, /*is_client=*/false);
326 }
327
328 // Test that the server records unknown when the client does not send metadata
TEST_P(MetadataExchangeTest,ClientDoesNotSendMetadata)329 TEST_P(MetadataExchangeTest, ClientDoesNotSendMetadata) {
330 Init(std::move(
331 Options().set_metric_names({grpc::OpenTelemetryPluginBuilder::
332 kServerCallDurationInstrumentName})),
333 /*enable_client_side_injector=*/false);
334 SendRPC();
335 const char* kMetricName = "grpc.server.call.duration";
336 auto data = ReadCurrentMetricsData(
337 [&](const absl::flat_hash_map<
338 std::string,
339 std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
340 data) { return !data.contains(kMetricName); });
341 ASSERT_EQ(data[kMetricName].size(), 1);
342 auto point_data =
343 absl::get_if<opentelemetry::sdk::metrics::HistogramPointData>(
344 &data[kMetricName][0].point_data);
345 ASSERT_NE(point_data, nullptr);
346 ASSERT_EQ(point_data->count_, 1);
347 const auto& attributes = data[kMetricName][0].attributes.GetAttributes();
348 EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.method")), kMethodName);
349 EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.status")), "OK");
350 EXPECT_EQ(
351 absl::get<std::string>(attributes.at("csm.workload_canonical_service")),
352 "canonical_service");
353 EXPECT_EQ(absl::get<std::string>(attributes.at("csm.mesh_id")), "mesh-id");
354 EXPECT_EQ(absl::get<std::string>(attributes.at("csm.remote_workload_type")),
355 "unknown");
356 EXPECT_EQ(absl::get<std::string>(
357 attributes.at("csm.remote_workload_canonical_service")),
358 "unknown");
359 }
360
TEST_P(MetadataExchangeTest,VerifyCsmServiceLabels)361 TEST_P(MetadataExchangeTest, VerifyCsmServiceLabels) {
362 Init(std::move(
363 Options()
364 .set_metric_names({grpc::OpenTelemetryPluginBuilder::
365 kClientAttemptDurationInstrumentName})
366 .set_labels_to_inject(
367 {{OptionalLabelKey::kXdsServiceName,
368 grpc_core::RefCountedStringValue("myservice")},
369 {OptionalLabelKey::kXdsServiceNamespace,
370 grpc_core::RefCountedStringValue("mynamespace")}})));
371 SendRPC();
372 const char* kMetricName = "grpc.client.attempt.duration";
373 auto data = ReadCurrentMetricsData(
374 [&](const absl::flat_hash_map<
375 std::string,
376 std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
377 data) { return !data.contains(kMetricName); });
378 ASSERT_EQ(data[kMetricName].size(), 1);
379 const auto& attributes = data[kMetricName][0].attributes.GetAttributes();
380 EXPECT_EQ(absl::get<std::string>(attributes.at("csm.service_name")),
381 "myservice");
382 EXPECT_EQ(absl::get<std::string>(attributes.at("csm.service_namespace_name")),
383 "mynamespace");
384 }
385
386 // Test that metadata exchange works and corresponding service mesh labels are
387 // received and recorded even if the server sends a trailers-only response.
TEST_P(MetadataExchangeTest,Retries)388 TEST_P(MetadataExchangeTest, Retries) {
389 Init(std::move(
390 Options()
391 .set_metric_names({grpc::OpenTelemetryPluginBuilder::
392 kClientAttemptDurationInstrumentName})
393 .set_service_config(
394 "{\n"
395 " \"methodConfig\": [ {\n"
396 " \"name\": [\n"
397 " { \"service\": \"grpc.testing.EchoTestService\" }\n"
398 " ],\n"
399 " \"retryPolicy\": {\n"
400 " \"maxAttempts\": 3,\n"
401 " \"initialBackoff\": \"0.1s\",\n"
402 " \"maxBackoff\": \"120s\",\n"
403 " \"backoffMultiplier\": 1,\n"
404 " \"retryableStatusCodes\": [ \"ABORTED\" ]\n"
405 " }\n"
406 " } ]\n"
407 "}")));
408 EchoRequest request;
409 request.mutable_param()->mutable_expected_error()->set_code(
410 StatusCode::ABORTED);
411 EchoResponse response;
412 grpc::ClientContext context;
413 grpc::Status status = stub_->Echo(&context, request, &response);
414 const char* kMetricName = "grpc.client.attempt.duration";
415 auto data = ReadCurrentMetricsData(
416 [&](const absl::flat_hash_map<
417 std::string,
418 std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
419 data) {
420 return !data.contains(kMetricName) ||
421 absl::get<opentelemetry::sdk::metrics::HistogramPointData>(
422 data.at(kMetricName)[0].point_data)
423 .count_ != 3;
424 });
425 ASSERT_EQ(absl::get<opentelemetry::sdk::metrics::HistogramPointData>(
426 data.at(kMetricName)[0].point_data)
427 .count_,
428 3);
429 VerifyServiceMeshAttributes(data.at(kMetricName)[0].attributes,
430 /*is_client=*/true);
431 }
432
433 // Creates a serialized slice with labels for metadata exchange based on \a
434 // resource.
RemoteMetadataSliceFromResource(const opentelemetry::sdk::resource::Resource & resource)435 grpc_core::Slice RemoteMetadataSliceFromResource(
436 const opentelemetry::sdk::resource::Resource& resource) {
437 return grpc::internal::ServiceMeshLabelsInjector(resource.GetAttributes())
438 .TestOnlySerializedLabels()
439 .Ref();
440 }
441
LabelsFromIterable(grpc::internal::MeshLabelsIterable * iterable)442 std::vector<std::pair<absl::string_view, absl::string_view>> LabelsFromIterable(
443 grpc::internal::MeshLabelsIterable* iterable) {
444 std::vector<std::pair<absl::string_view, absl::string_view>> labels;
445 while (true) {
446 auto label = iterable->Next();
447 if (!label.has_value()) break;
448 labels.push_back(*std::move(label));
449 }
450 EXPECT_EQ(labels.size(), iterable->Size());
451 return labels;
452 }
453
PrettyPrintLabels(const std::vector<std::pair<absl::string_view,absl::string_view>> & labels)454 std::string PrettyPrintLabels(
455 const std::vector<std::pair<absl::string_view, absl::string_view>>&
456 labels) {
457 std::vector<std::string> strings;
458 strings.reserve(labels.size());
459 for (const auto& pair : labels) {
460 strings.push_back(
461 absl::StrFormat("{\"%s\" : \"%s\"}", pair.first, pair.second));
462 }
463 return absl::StrJoin(strings, ", ");
464 }
465
TEST(MeshLabelsIterableTest,NoRemoteMetadata)466 TEST(MeshLabelsIterableTest, NoRemoteMetadata) {
467 std::vector<std::pair<absl::string_view, std::string>> local_labels = {
468 {"csm.workload_canonical_service", "canonical_service"},
469 {"csm.mesh_id", "mesh"}};
470 grpc::internal::MeshLabelsIterable iterable(local_labels, grpc_core::Slice());
471 auto labels = LabelsFromIterable(&iterable);
472 EXPECT_FALSE(iterable.GotRemoteLabels());
473 EXPECT_THAT(
474 labels,
475 ElementsAre(Pair("csm.workload_canonical_service", "canonical_service"),
476 Pair("csm.mesh_id", "mesh"),
477 Pair("csm.remote_workload_type", "unknown"),
478 Pair("csm.remote_workload_canonical_service", "unknown")))
479 << PrettyPrintLabels(labels);
480 }
481
TEST(MeshLabelsIterableTest,RemoteGceTypeMetadata)482 TEST(MeshLabelsIterableTest, RemoteGceTypeMetadata) {
483 std::vector<std::pair<absl::string_view, std::string>> local_labels = {
484 {"csm.workload_canonical_service", "canonical_service"},
485 {"csm.mesh_id", "mesh"}};
486 grpc::internal::MeshLabelsIterable iterable(
487 local_labels, RemoteMetadataSliceFromResource(TestGceResource()));
488 auto labels = LabelsFromIterable(&iterable);
489 EXPECT_TRUE(iterable.GotRemoteLabels());
490 EXPECT_THAT(
491 labels,
492 ElementsAre(
493 Pair("csm.workload_canonical_service", "canonical_service"),
494 Pair("csm.mesh_id", "mesh"),
495 Pair("csm.remote_workload_type", "gcp_compute_engine"),
496 Pair("csm.remote_workload_canonical_service", "canonical_service"),
497 Pair("csm.remote_workload_name", "workload"),
498 Pair("csm.remote_workload_location", "zone"),
499 Pair("csm.remote_workload_project_id", "id")))
500 << PrettyPrintLabels(labels);
501 }
502
TEST(MeshLabelsIterableTest,RemoteGkeTypeMetadata)503 TEST(MeshLabelsIterableTest, RemoteGkeTypeMetadata) {
504 std::vector<std::pair<absl::string_view, std::string>> local_labels = {
505 {"csm.workload_canonical_service", "canonical_service"},
506 {"csm.mesh_id", "mesh"}};
507 grpc::internal::MeshLabelsIterable iterable(
508 local_labels, RemoteMetadataSliceFromResource(TestGkeResource()));
509 auto labels = LabelsFromIterable(&iterable);
510 EXPECT_TRUE(iterable.GotRemoteLabels());
511 EXPECT_THAT(
512 labels,
513 ElementsAre(
514 Pair("csm.workload_canonical_service", "canonical_service"),
515 Pair("csm.mesh_id", "mesh"),
516 Pair("csm.remote_workload_type", "gcp_kubernetes_engine"),
517 Pair("csm.remote_workload_canonical_service", "canonical_service"),
518 Pair("csm.remote_workload_name", "workload"),
519 Pair("csm.remote_workload_namespace_name", "namespace"),
520 Pair("csm.remote_workload_cluster_name", "cluster"),
521 Pair("csm.remote_workload_location", "region"),
522 Pair("csm.remote_workload_project_id", "id")))
523 << PrettyPrintLabels(labels);
524 }
525
TEST(MeshLabelsIterableTest,RemoteUnknownTypeMetadata)526 TEST(MeshLabelsIterableTest, RemoteUnknownTypeMetadata) {
527 std::vector<std::pair<absl::string_view, std::string>> local_labels = {
528 {"csm.workload_canonical_service", "canonical_service"},
529 {"csm.mesh_id", "mesh"}};
530 grpc::internal::MeshLabelsIterable iterable(
531 local_labels, RemoteMetadataSliceFromResource(TestUnknownResource()));
532 auto labels = LabelsFromIterable(&iterable);
533 EXPECT_TRUE(iterable.GotRemoteLabels());
534 EXPECT_THAT(
535 labels,
536 ElementsAre(
537 Pair("csm.workload_canonical_service", "canonical_service"),
538 Pair("csm.mesh_id", "mesh"),
539 Pair("csm.remote_workload_type", "random"),
540 Pair("csm.remote_workload_canonical_service", "canonical_service")))
541 << PrettyPrintLabels(labels);
542 }
543
TEST(MeshLabelsIterableTest,TestResetIteratorPosition)544 TEST(MeshLabelsIterableTest, TestResetIteratorPosition) {
545 std::vector<std::pair<absl::string_view, std::string>> local_labels = {
546 {"csm.workload_canonical_service", "canonical_service"},
547 {"csm.mesh_id", "mesh"}};
548 grpc::internal::MeshLabelsIterable iterable(local_labels, grpc_core::Slice());
549 auto labels = LabelsFromIterable(&iterable);
550 auto expected_labels_matcher = ElementsAre(
551 Pair("csm.workload_canonical_service", "canonical_service"),
552 Pair("csm.mesh_id", "mesh"), Pair("csm.remote_workload_type", "unknown"),
553 Pair("csm.remote_workload_canonical_service", "unknown"));
554 EXPECT_THAT(labels, expected_labels_matcher) << PrettyPrintLabels(labels);
555 // Resetting the iterable should return the entire list again.
556 iterable.ResetIteratorPosition();
557 labels = LabelsFromIterable(&iterable);
558 EXPECT_THAT(labels, expected_labels_matcher) << PrettyPrintLabels(labels);
559 }
560
561 INSTANTIATE_TEST_SUITE_P(
562 MetadataExchange, MetadataExchangeTest,
563 ::testing::Values(TestScenario(TestScenario::ResourceType::kGke),
564 TestScenario(TestScenario::ResourceType::kGce),
565 TestScenario(TestScenario::ResourceType::kUnknown)),
566 &TestScenario::Name);
567
568 } // namespace
569 } // namespace testing
570 } // namespace grpc
571
main(int argc,char ** argv)572 int main(int argc, char** argv) {
573 grpc::testing::TestEnvironment env(&argc, argv);
574 ::testing::InitGoogleTest(&argc, argv);
575 grpc_core::SetEnv("CSM_WORKLOAD_NAME", "workload");
576 grpc_core::SetEnv("CSM_CANONICAL_SERVICE_NAME", "canonical_service");
577 grpc_core::SetEnv("CSM_MESH_ID", "mesh-id");
578 return RUN_ALL_TESTS();
579 }
580