• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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