xref: /aosp_15_r20/external/grpc-grpc/test/cpp/ext/csm/metadata_exchange_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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 "absl/functional/any_invocable.h"
22 #include "gmock/gmock.h"
23 #include "google/cloud/opentelemetry/resource_detector.h"
24 #include "gtest/gtest.h"
25 #include "opentelemetry/metrics/provider.h"
26 #include "opentelemetry/sdk/metrics/meter_provider.h"
27 #include "opentelemetry/sdk/metrics/metric_reader.h"
28 
29 #include <grpcpp/ext/otel_plugin.h>
30 #include <grpcpp/grpcpp.h>
31 
32 #include "src/core/lib/channel/call_tracer.h"
33 #include "src/core/lib/config/core_configuration.h"
34 #include "src/core/lib/gpr/tmpfile.h"
35 #include "src/core/lib/gprpp/env.h"
36 #include "src/cpp/ext/csm/csm_observability.h"
37 #include "src/cpp/ext/otel/otel_plugin.h"
38 #include "test/core/util/test_config.h"
39 #include "test/cpp/end2end/test_service_impl.h"
40 #include "test/cpp/ext/otel/otel_test_library.h"
41 
42 namespace grpc {
43 namespace testing {
44 namespace {
45 
46 using OptionalLabelKey =
47     grpc_core::ClientCallTracer::CallAttemptTracer::OptionalLabelKey;
48 using ::testing::ElementsAre;
49 using ::testing::Pair;
50 
TestGkeResource()51 opentelemetry::sdk::resource::Resource TestGkeResource() {
52   opentelemetry::sdk::common::AttributeMap attributes;
53   attributes.SetAttribute("cloud.platform", "gcp_kubernetes_engine");
54   attributes.SetAttribute("k8s.pod.name", "pod");
55   attributes.SetAttribute("k8s.container.name", "container");
56   attributes.SetAttribute("k8s.namespace.name", "namespace");
57   attributes.SetAttribute("k8s.cluster.name", "cluster");
58   attributes.SetAttribute("cloud.region", "region");
59   attributes.SetAttribute("cloud.account.id", "id");
60   return opentelemetry::sdk::resource::Resource::Create(attributes);
61 }
62 
TestGceResource()63 opentelemetry::sdk::resource::Resource TestGceResource() {
64   opentelemetry::sdk::common::AttributeMap attributes;
65   attributes.SetAttribute("cloud.platform", "gcp_compute_engine");
66   attributes.SetAttribute("cloud.availability_zone", "zone");
67   attributes.SetAttribute("cloud.account.id", "id");
68   return opentelemetry::sdk::resource::Resource::Create(attributes);
69 }
70 
TestUnknownResource()71 opentelemetry::sdk::resource::Resource TestUnknownResource() {
72   opentelemetry::sdk::common::AttributeMap attributes;
73   attributes.SetAttribute("cloud.platform", "random");
74   return opentelemetry::sdk::resource::Resource::Create(attributes);
75 }
76 
77 class TestScenario {
78  public:
79   enum class ResourceType : std::uint8_t { kGke, kGce, kUnknown };
80   enum class XdsBootstrapSource : std::uint8_t { kFromFile, kFromConfig };
81 
TestScenario(ResourceType type,XdsBootstrapSource bootstrap_source)82   explicit TestScenario(ResourceType type, XdsBootstrapSource bootstrap_source)
83       : type_(type), bootstrap_source_(bootstrap_source) {}
84 
GetTestResource() const85   opentelemetry::sdk::resource::Resource GetTestResource() const {
86     switch (type_) {
87       case ResourceType::kGke:
88         return TestGkeResource();
89       case ResourceType::kGce:
90         return TestGceResource();
91       case ResourceType::kUnknown:
92         return TestUnknownResource();
93     }
94   }
95 
Name(const::testing::TestParamInfo<TestScenario> & info)96   static std::string Name(const ::testing::TestParamInfo<TestScenario>& info) {
97     std::string ret_val;
98     switch (info.param.type_) {
99       case ResourceType::kGke:
100         ret_val += "Gke";
101         break;
102       case ResourceType::kGce:
103         ret_val += "Gce";
104         break;
105       case ResourceType::kUnknown:
106         ret_val += "Unknown";
107         break;
108     }
109     switch (info.param.bootstrap_source_) {
110       case TestScenario::XdsBootstrapSource::kFromFile:
111         ret_val += "BootstrapFromFile";
112         break;
113       case TestScenario::XdsBootstrapSource::kFromConfig:
114         ret_val += "BootstrapFromConfig";
115         break;
116     }
117     return ret_val;
118   }
119 
type() const120   ResourceType type() const { return type_; }
121 
bootstrap_source() const122   XdsBootstrapSource bootstrap_source() const { return bootstrap_source_; }
123 
124  private:
125   ResourceType type_;
126   XdsBootstrapSource bootstrap_source_;
127 };
128 
129 // A PluginOption that injects `ServiceMeshLabelsInjector`. (This is different
130 // from CsmOpenTelemetryPluginOption since it does not restrict itself to just
131 // CSM channels and servers.)
132 class MeshLabelsPluginOption
133     : public grpc::internal::InternalOpenTelemetryPluginOption {
134  public:
MeshLabelsPluginOption(const opentelemetry::sdk::common::AttributeMap & map)135   explicit MeshLabelsPluginOption(
136       const opentelemetry::sdk::common::AttributeMap& map)
137       : labels_injector_(
138             std::make_unique<grpc::internal::ServiceMeshLabelsInjector>(map)) {}
139 
IsActiveOnClientChannel(absl::string_view) const140   bool IsActiveOnClientChannel(absl::string_view /*target*/) const override {
141     return true;
142   }
143 
IsActiveOnServer(const grpc_core::ChannelArgs &) const144   bool IsActiveOnServer(const grpc_core::ChannelArgs& /*args*/) const override {
145     return true;
146   }
147 
labels_injector() const148   const grpc::internal::LabelsInjector* labels_injector() const override {
149     return labels_injector_.get();
150   }
151 
152  private:
153   std::unique_ptr<grpc::internal::ServiceMeshLabelsInjector> labels_injector_;
154 };
155 
156 class MetadataExchangeTest
157     : public OpenTelemetryPluginEnd2EndTest,
158       public ::testing::WithParamInterface<TestScenario> {
159  protected:
Init(const std::vector<absl::string_view> & metric_names,bool enable_client_side_injector=true,std::map<grpc_core::ClientCallTracer::CallAttemptTracer::OptionalLabelKey,grpc_core::RefCountedStringValue> labels_to_inject={})160   void Init(
161       const std::vector<absl::string_view>& metric_names,
162       bool enable_client_side_injector = true,
163       std::map<grpc_core::ClientCallTracer::CallAttemptTracer::OptionalLabelKey,
164                grpc_core::RefCountedStringValue>
165           labels_to_inject = {}) {
166     const char* kBootstrap =
167         "{\"node\": {\"id\": "
168         "\"projects/1234567890/networks/mesh:mesh-id/nodes/"
169         "01234567-89ab-4def-8123-456789abcdef\"}}";
170     switch (GetParam().bootstrap_source()) {
171       case TestScenario::XdsBootstrapSource::kFromFile: {
172         ASSERT_EQ(bootstrap_file_name_, nullptr);
173         FILE* bootstrap_file =
174             gpr_tmpfile("xds_bootstrap", &bootstrap_file_name_);
175         fputs(kBootstrap, bootstrap_file);
176         fclose(bootstrap_file);
177         grpc_core::SetEnv("GRPC_XDS_BOOTSTRAP", bootstrap_file_name_);
178         break;
179       }
180       case TestScenario::XdsBootstrapSource::kFromConfig:
181         grpc_core::SetEnv("GRPC_XDS_BOOTSTRAP_CONFIG", kBootstrap);
182         break;
183     }
184     OpenTelemetryPluginEnd2EndTest::Init(std::move(
185         Options()
186             .set_metric_names(metric_names)
187             .add_plugin_option(std::make_unique<MeshLabelsPluginOption>(
188                 GetParam().GetTestResource().GetAttributes()))
189             .set_labels_to_inject(std::move(labels_to_inject))
190             .set_channel_scope_filter(
191                 [enable_client_side_injector](
__anond9016ec40202( const OpenTelemetryPluginBuilder::ChannelScope& ) 192                     const OpenTelemetryPluginBuilder::ChannelScope& /*scope*/) {
193                   return enable_client_side_injector;
194                 })));
195   }
196 
~MetadataExchangeTest()197   ~MetadataExchangeTest() override {
198     grpc_core::UnsetEnv("GRPC_XDS_BOOTSTRAP_CONFIG");
199     grpc_core::UnsetEnv("GRPC_XDS_BOOTSTRAP");
200     if (bootstrap_file_name_ != nullptr) {
201       remove(bootstrap_file_name_);
202       gpr_free(bootstrap_file_name_);
203     }
204   }
205 
VerifyServiceMeshAttributes(const std::map<std::string,opentelemetry::sdk::common::OwnedAttributeValue> & attributes,bool is_client)206   void VerifyServiceMeshAttributes(
207       const std::map<std::string,
208                      opentelemetry::sdk::common::OwnedAttributeValue>&
209           attributes,
210       bool is_client) {
211     EXPECT_EQ(
212         absl::get<std::string>(attributes.at("csm.workload_canonical_service")),
213         "canonical_service");
214     EXPECT_EQ(absl::get<std::string>(attributes.at("csm.mesh_id")), "mesh-id");
215     EXPECT_EQ(absl::get<std::string>(
216                   attributes.at("csm.remote_workload_canonical_service")),
217               "canonical_service");
218     if (is_client) {
219       EXPECT_EQ(absl::get<std::string>(attributes.at("csm.service_name")),
220                 "unknown");
221       EXPECT_EQ(
222           absl::get<std::string>(attributes.at("csm.service_namespace_name")),
223           "unknown");
224     } else {
225       // The CSM optional labels should not be present in server metrics.
226       EXPECT_THAT(attributes, ::testing::Not(::testing::Contains(
227                                   ::testing::Key("csm.service_name"))));
228       EXPECT_THAT(attributes, ::testing::Not(::testing::Contains(::testing::Key(
229                                   "csm.service_namespace_name"))));
230     }
231     switch (GetParam().type()) {
232       case TestScenario::ResourceType::kGke:
233         EXPECT_EQ(
234             absl::get<std::string>(attributes.at("csm.remote_workload_type")),
235             "gcp_kubernetes_engine");
236         EXPECT_EQ(
237             absl::get<std::string>(attributes.at("csm.remote_workload_name")),
238             "workload");
239         EXPECT_EQ(absl::get<std::string>(
240                       attributes.at("csm.remote_workload_namespace_name")),
241                   "namespace");
242         EXPECT_EQ(absl::get<std::string>(
243                       attributes.at("csm.remote_workload_cluster_name")),
244                   "cluster");
245         EXPECT_EQ(absl::get<std::string>(
246                       attributes.at("csm.remote_workload_location")),
247                   "region");
248         EXPECT_EQ(absl::get<std::string>(
249                       attributes.at("csm.remote_workload_project_id")),
250                   "id");
251         break;
252       case TestScenario::ResourceType::kGce:
253         EXPECT_EQ(
254             absl::get<std::string>(attributes.at("csm.remote_workload_type")),
255             "gcp_compute_engine");
256         EXPECT_EQ(
257             absl::get<std::string>(attributes.at("csm.remote_workload_name")),
258             "workload");
259         EXPECT_EQ(absl::get<std::string>(
260                       attributes.at("csm.remote_workload_location")),
261                   "zone");
262         EXPECT_EQ(absl::get<std::string>(
263                       attributes.at("csm.remote_workload_project_id")),
264                   "id");
265         break;
266       case TestScenario::ResourceType::kUnknown:
267         EXPECT_EQ(
268             absl::get<std::string>(attributes.at("csm.remote_workload_type")),
269             "random");
270         break;
271     }
272   }
273 
VerifyNoServiceMeshAttributes(const std::map<std::string,opentelemetry::sdk::common::OwnedAttributeValue> & attributes)274   void VerifyNoServiceMeshAttributes(
275       const std::map<std::string,
276                      opentelemetry::sdk::common::OwnedAttributeValue>&
277           attributes) {
278     EXPECT_EQ(attributes.find("csm.remote_workload_type"), attributes.end());
279   }
280 
281  private:
282   char* bootstrap_file_name_ = nullptr;
283 };
284 
285 // Verify that grpc.client.attempt.started does not get service mesh attributes
TEST_P(MetadataExchangeTest,ClientAttemptStarted)286 TEST_P(MetadataExchangeTest, ClientAttemptStarted) {
287   Init(/*metric_names=*/{
288       grpc::OpenTelemetryPluginBuilder::kClientAttemptStartedInstrumentName});
289   SendRPC();
290   const char* kMetricName = "grpc.client.attempt.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   auto client_started_value = absl::get_if<int64_t>(&point_data->value_);
301   ASSERT_NE(client_started_value, nullptr);
302   EXPECT_EQ(*client_started_value, 1);
303   const auto& attributes = data[kMetricName][0].attributes.GetAttributes();
304   EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.method")), kMethodName);
305   EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.target")),
306             canonical_server_address_);
307   VerifyNoServiceMeshAttributes(attributes);
308 }
309 
TEST_P(MetadataExchangeTest,ClientAttemptDuration)310 TEST_P(MetadataExchangeTest, ClientAttemptDuration) {
311   Init(/*metric_names=*/{
312       grpc::OpenTelemetryPluginBuilder::kClientAttemptDurationInstrumentName});
313   SendRPC();
314   const char* kMetricName = "grpc.client.attempt.duration";
315   auto data = ReadCurrentMetricsData(
316       [&](const absl::flat_hash_map<
317           std::string,
318           std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
319               data) { return !data.contains(kMetricName); });
320   ASSERT_EQ(data[kMetricName].size(), 1);
321   auto point_data =
322       absl::get_if<opentelemetry::sdk::metrics::HistogramPointData>(
323           &data[kMetricName][0].point_data);
324   ASSERT_NE(point_data, nullptr);
325   ASSERT_EQ(point_data->count_, 1);
326   const auto& attributes = data[kMetricName][0].attributes.GetAttributes();
327   EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.method")), kMethodName);
328   EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.target")),
329             canonical_server_address_);
330   EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.status")), "OK");
331   VerifyServiceMeshAttributes(attributes, /*is_client=*/true);
332 }
333 
334 // Verify that grpc.server.call.started does not get service mesh attributes
TEST_P(MetadataExchangeTest,ServerCallStarted)335 TEST_P(MetadataExchangeTest, ServerCallStarted) {
336   Init(
337       /*metric_names=*/{
338           grpc::OpenTelemetryPluginBuilder::kServerCallStartedInstrumentName});
339   SendRPC();
340   const char* kMetricName = "grpc.server.call.started";
341   auto data = ReadCurrentMetricsData(
342       [&](const absl::flat_hash_map<
343           std::string,
344           std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
345               data) { return !data.contains(kMetricName); });
346   ASSERT_EQ(data[kMetricName].size(), 1);
347   auto point_data = absl::get_if<opentelemetry::sdk::metrics::SumPointData>(
348       &data[kMetricName][0].point_data);
349   ASSERT_NE(point_data, nullptr);
350   ASSERT_EQ(absl::get<int64_t>(point_data->value_), 1);
351   const auto& attributes = data[kMetricName][0].attributes.GetAttributes();
352   EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.method")), kMethodName);
353   VerifyNoServiceMeshAttributes(attributes);
354 }
355 
TEST_P(MetadataExchangeTest,ServerCallDuration)356 TEST_P(MetadataExchangeTest, ServerCallDuration) {
357   Init(
358       /*metric_names=*/{
359           grpc::OpenTelemetryPluginBuilder::kServerCallDurationInstrumentName});
360   SendRPC();
361   const char* kMetricName = "grpc.server.call.duration";
362   auto data = ReadCurrentMetricsData(
363       [&](const absl::flat_hash_map<
364           std::string,
365           std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
366               data) { return !data.contains(kMetricName); });
367   ASSERT_EQ(data[kMetricName].size(), 1);
368   auto point_data =
369       absl::get_if<opentelemetry::sdk::metrics::HistogramPointData>(
370           &data[kMetricName][0].point_data);
371   ASSERT_NE(point_data, nullptr);
372   ASSERT_EQ(point_data->count_, 1);
373   const auto& attributes = data[kMetricName][0].attributes.GetAttributes();
374   EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.method")), kMethodName);
375   EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.status")), "OK");
376   VerifyServiceMeshAttributes(attributes, /*is_client=*/false);
377 }
378 
379 // Test that the server records unknown when the client does not send metadata
TEST_P(MetadataExchangeTest,ClientDoesNotSendMetadata)380 TEST_P(MetadataExchangeTest, ClientDoesNotSendMetadata) {
381   Init(
382       /*metric_names=*/{grpc::OpenTelemetryPluginBuilder::
383                             kServerCallDurationInstrumentName},
384       /*enable_client_side_injector=*/false);
385   SendRPC();
386   const char* kMetricName = "grpc.server.call.duration";
387   auto data = ReadCurrentMetricsData(
388       [&](const absl::flat_hash_map<
389           std::string,
390           std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
391               data) { return !data.contains(kMetricName); });
392   ASSERT_EQ(data[kMetricName].size(), 1);
393   auto point_data =
394       absl::get_if<opentelemetry::sdk::metrics::HistogramPointData>(
395           &data[kMetricName][0].point_data);
396   ASSERT_NE(point_data, nullptr);
397   ASSERT_EQ(point_data->count_, 1);
398   const auto& attributes = data[kMetricName][0].attributes.GetAttributes();
399   EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.method")), kMethodName);
400   EXPECT_EQ(absl::get<std::string>(attributes.at("grpc.status")), "OK");
401   EXPECT_EQ(
402       absl::get<std::string>(attributes.at("csm.workload_canonical_service")),
403       "canonical_service");
404   EXPECT_EQ(absl::get<std::string>(attributes.at("csm.mesh_id")), "mesh-id");
405   EXPECT_EQ(absl::get<std::string>(attributes.at("csm.remote_workload_type")),
406             "unknown");
407 }
408 
TEST_P(MetadataExchangeTest,VerifyCsmServiceLabels)409 TEST_P(MetadataExchangeTest, VerifyCsmServiceLabels) {
410   Init(/*metric_names=*/{grpc::OpenTelemetryPluginBuilder::
411                              kClientAttemptDurationInstrumentName},
412        /*enable_client_side_injector=*/true,
413        // Injects CSM service labels to be recorded in the call.
414        {{OptionalLabelKey::kXdsServiceName,
415          grpc_core::RefCountedStringValue("myservice")},
416         {OptionalLabelKey::kXdsServiceNamespace,
417          grpc_core::RefCountedStringValue("mynamespace")}});
418   SendRPC();
419   const char* kMetricName = "grpc.client.attempt.duration";
420   auto data = ReadCurrentMetricsData(
421       [&](const absl::flat_hash_map<
422           std::string,
423           std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
424               data) { return !data.contains(kMetricName); });
425   ASSERT_EQ(data[kMetricName].size(), 1);
426   const auto& attributes = data[kMetricName][0].attributes.GetAttributes();
427   EXPECT_EQ(absl::get<std::string>(attributes.at("csm.service_name")),
428             "myservice");
429   EXPECT_EQ(absl::get<std::string>(attributes.at("csm.service_namespace_name")),
430             "mynamespace");
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(
564         TestScenario(TestScenario::ResourceType::kGke,
565                      TestScenario::XdsBootstrapSource::kFromConfig),
566         TestScenario(TestScenario::ResourceType::kGke,
567                      TestScenario::XdsBootstrapSource::kFromFile),
568         TestScenario(TestScenario::ResourceType::kGce,
569                      TestScenario::XdsBootstrapSource::kFromConfig),
570         TestScenario(TestScenario::ResourceType::kGce,
571                      TestScenario::XdsBootstrapSource::kFromFile),
572         TestScenario(TestScenario::ResourceType::kUnknown,
573                      TestScenario::XdsBootstrapSource::kFromConfig),
574         TestScenario(TestScenario::ResourceType::kUnknown,
575                      TestScenario::XdsBootstrapSource::kFromFile)),
576     &TestScenario::Name);
577 
578 }  // namespace
579 }  // namespace testing
580 }  // namespace grpc
581 
main(int argc,char ** argv)582 int main(int argc, char** argv) {
583   grpc::testing::TestEnvironment env(&argc, argv);
584   ::testing::InitGoogleTest(&argc, argv);
585   grpc_core::SetEnv("CSM_WORKLOAD_NAME", "workload");
586   grpc_core::SetEnv("CSM_CANONICAL_SERVICE_NAME", "canonical_service");
587   return RUN_ALL_TESTS();
588 }
589