diff --git a/src/cpp/ext/csm/metadata_exchange.cc b/src/cpp/ext/csm/metadata_exchange.cc index 870818161c3..e16a68b5b93 100644 --- a/src/cpp/ext/csm/metadata_exchange.cc +++ b/src/cpp/ext/csm/metadata_exchange.cc @@ -89,8 +89,9 @@ constexpr absl::string_view kPeerCanonicalServiceAttribute = "csm.remote_workload_canonical_service"; // Type values used by Google Cloud Resource Detector constexpr absl::string_view kGkeType = "gcp_kubernetes_engine"; +constexpr absl::string_view kGceType = "gcp_compute_engine"; -enum class GcpResourceType : std::uint8_t { kGke, kUnknown }; +enum class GcpResourceType : std::uint8_t { kGke, kGce, kUnknown }; // A minimal class for helping with the information we need from the xDS // bootstrap file for GSM Observability reasons. @@ -151,6 +152,8 @@ std::string GetXdsBootstrapContents() { GcpResourceType StringToGcpResourceType(absl::string_view type) { if (type == kGkeType) { return GcpResourceType::kGke; + } else if (type == kGceType) { + return GcpResourceType::kGce; } return GcpResourceType::kUnknown; } @@ -219,39 +222,19 @@ class MeshLabelsIterable : public LabelsIterable { if (pos_ < local_labels_size) { return local_labels_[pos_++]; } - if (++pos_ == local_labels_size + 1) { - return std::make_pair(kPeerTypeAttribute, - GetStringValueFromUpbStruct( - struct_pb.struct_pb, kMetadataExchangeTypeKey, - struct_pb.arena.ptr())); - } - // Only handle GKE type for now. - switch (remote_type_) { - case GcpResourceType::kGke: - if ((pos_ - 2 - local_labels_size) >= kGkeAttributeList.size()) { - return absl::nullopt; - } - return std::make_pair( - kGkeAttributeList[pos_ - 2 - local_labels_size].otel_attribute, - GetStringValueFromUpbStruct( - struct_pb.struct_pb, - kGkeAttributeList[pos_ - 2 - local_labels_size] - .metadata_attribute, - struct_pb.arena.ptr())); - case GcpResourceType::kUnknown: - return absl::nullopt; + const size_t fixed_attribute_end = + local_labels_size + kFixedAttributes.size(); + if (pos_ < fixed_attribute_end) { + return NextFromAttributeList(struct_pb, kFixedAttributes, + local_labels_size); } + return NextFromAttributeList(struct_pb, GetAttributesForType(remote_type_), + fixed_attribute_end); } size_t Size() const override { - auto& struct_pb = GetDecodedMetadata(); - if (struct_pb.struct_pb == nullptr) { - return local_labels_.size(); - } - if (remote_type_ != GcpResourceType::kGke) { - return local_labels_.size() + 1; - } - return local_labels_.size() + kGkeAttributeList.size() + 1; + return local_labels_.size() + kFixedAttributes.size() + + GetAttributesForType(remote_type_).size(); } void ResetIteratorPosition() override { pos_ = 0; } @@ -263,7 +246,7 @@ class MeshLabelsIterable : public LabelsIterable { } private: - struct GkeAttribute { + struct RemoteAttribute { absl::string_view otel_attribute; absl::string_view metadata_attribute; }; @@ -273,18 +256,56 @@ class MeshLabelsIterable : public LabelsIterable { google_protobuf_Struct* struct_pb = nullptr; }; - static constexpr std::array kGkeAttributeList = { - GkeAttribute{kPeerWorkloadNameAttribute, - kMetadataExchangeWorkloadNameKey}, - GkeAttribute{kPeerNamespaceNameAttribute, - kMetadataExchangeNamespaceNameKey}, - GkeAttribute{kPeerClusterNameAttribute, kMetadataExchangeClusterNameKey}, - GkeAttribute{kPeerLocationAttribute, kMetadataExchangeLocationKey}, - GkeAttribute{kPeerProjectIdAttribute, kMetadataExchangeProjectIdKey}, - GkeAttribute{kPeerCanonicalServiceAttribute, - kMetadataExchangeCanonicalServiceKey}, + static constexpr std::array kFixedAttributes = { + RemoteAttribute{kPeerTypeAttribute, kMetadataExchangeTypeKey}, + RemoteAttribute{kPeerCanonicalServiceAttribute, + kMetadataExchangeCanonicalServiceKey}, }; + static constexpr std::array kGkeAttributeList = { + RemoteAttribute{kPeerWorkloadNameAttribute, + kMetadataExchangeWorkloadNameKey}, + RemoteAttribute{kPeerNamespaceNameAttribute, + kMetadataExchangeNamespaceNameKey}, + RemoteAttribute{kPeerClusterNameAttribute, + kMetadataExchangeClusterNameKey}, + RemoteAttribute{kPeerLocationAttribute, kMetadataExchangeLocationKey}, + RemoteAttribute{kPeerProjectIdAttribute, kMetadataExchangeProjectIdKey}, + }; + static constexpr std::array kGceAttributeList = { + RemoteAttribute{kPeerWorkloadNameAttribute, + kMetadataExchangeWorkloadNameKey}, + RemoteAttribute{kPeerLocationAttribute, kMetadataExchangeLocationKey}, + RemoteAttribute{kPeerProjectIdAttribute, kMetadataExchangeProjectIdKey}, + }; + + static absl::Span GetAttributesForType( + GcpResourceType remote_type) { + switch (remote_type) { + case GcpResourceType::kGke: + return kGkeAttributeList; + case GcpResourceType::kGce: + return kGceAttributeList; + default: + return {}; + } + } + + absl::optional> + NextFromAttributeList(const StructPb& struct_pb, + absl::Span attributes, + size_t start_index) { + GPR_DEBUG_ASSERT(pos_ >= start_index); + const size_t index = pos_ - start_index; + if (index >= attributes.size()) return absl::nullopt; + ++pos_; + const auto& attribute = attributes[index]; + return std::make_pair(attribute.otel_attribute, + GetStringValueFromUpbStruct( + struct_pb.struct_pb, attribute.metadata_attribute, + struct_pb.arena.ptr())); + } + StructPb& GetDecodedMetadata() const { auto* slice = absl::get_if(&metadata_); if (slice == nullptr) { @@ -319,8 +340,12 @@ class MeshLabelsIterable : public LabelsIterable { uint32_t pos_ = 0; }; -constexpr std::array +constexpr std::array + MeshLabelsIterable::kFixedAttributes; +constexpr std::array MeshLabelsIterable::kGkeAttributeList; +constexpr std::array + MeshLabelsIterable::kGceAttributeList; } // namespace @@ -363,13 +388,13 @@ ServiceMeshLabelsInjector::ServiceMeshLabelsInjector( opentelemetry::sdk::resource::SemanticConventions::kK8sNamespaceName); absl::string_view cluster_name_value = GetStringValueFromAttributeMap( map, opentelemetry::sdk::resource::SemanticConventions::kK8sClusterName); - absl::string_view cluster_location_value = GetStringValueFromAttributeMap( + absl::string_view location_value = GetStringValueFromAttributeMap( map, opentelemetry::sdk::resource::SemanticConventions:: - kCloudRegion); // if regional - if (cluster_location_value == "unknown") { - cluster_location_value = GetStringValueFromAttributeMap( + kCloudAvailabilityZone); // if zonal + if (location_value == "unknown") { + location_value = GetStringValueFromAttributeMap( map, opentelemetry::sdk::resource::SemanticConventions:: - kCloudAvailabilityZone); // if zonal + kCloudRegion); // if regional } absl::string_view project_id_value = GetStringValueFromAttributeMap( map, opentelemetry::sdk::resource::SemanticConventions::kCloudAccountId); @@ -378,7 +403,8 @@ ServiceMeshLabelsInjector::ServiceMeshLabelsInjector( // Create metadata to be sent over wire. AddStringKeyValueToStructProto(metadata, kMetadataExchangeTypeKey, type_value, arena.ptr()); - // Only handle GKE for now + AddStringKeyValueToStructProto(metadata, kMetadataExchangeCanonicalServiceKey, + canonical_service_value, arena.ptr()); if (type_value == kGkeType) { AddStringKeyValueToStructProto(metadata, kMetadataExchangeWorkloadNameKey, workload_name_value, arena.ptr()); @@ -387,12 +413,16 @@ ServiceMeshLabelsInjector::ServiceMeshLabelsInjector( AddStringKeyValueToStructProto(metadata, kMetadataExchangeClusterNameKey, cluster_name_value, arena.ptr()); AddStringKeyValueToStructProto(metadata, kMetadataExchangeLocationKey, - cluster_location_value, arena.ptr()); + location_value, arena.ptr()); + AddStringKeyValueToStructProto(metadata, kMetadataExchangeProjectIdKey, + project_id_value, arena.ptr()); + } else if (type_value == kGceType) { + AddStringKeyValueToStructProto(metadata, kMetadataExchangeWorkloadNameKey, + workload_name_value, arena.ptr()); + AddStringKeyValueToStructProto(metadata, kMetadataExchangeLocationKey, + location_value, arena.ptr()); AddStringKeyValueToStructProto(metadata, kMetadataExchangeProjectIdKey, project_id_value, arena.ptr()); - AddStringKeyValueToStructProto(metadata, - kMetadataExchangeCanonicalServiceKey, - canonical_service_value, arena.ptr()); } size_t output_length; diff --git a/test/cpp/ext/csm/metadata_exchange_test.cc b/test/cpp/ext/csm/metadata_exchange_test.cc index 84d96b26872..b0c06a08f8c 100644 --- a/test/cpp/ext/csm/metadata_exchange_test.cc +++ b/test/cpp/ext/csm/metadata_exchange_test.cc @@ -45,7 +45,7 @@ namespace { class TestScenario { public: - enum class ResourceType : std::uint8_t { kGke, kUnknown }; + enum class ResourceType : std::uint8_t { kGke, kGce, kUnknown }; enum class XdsBootstrapSource : std::uint8_t { kFromFile, kFromConfig }; explicit TestScenario(ResourceType type, XdsBootstrapSource bootstrap_source) @@ -55,6 +55,8 @@ class TestScenario { switch (type_) { case ResourceType::kGke: return TestGkeResource(); + case ResourceType::kGce: + return TestGceResource(); case ResourceType::kUnknown: return TestUnknownResource(); } @@ -66,6 +68,9 @@ class TestScenario { case ResourceType::kGke: ret_val += "Gke"; break; + case ResourceType::kGce: + ret_val += "Gce"; + break; case ResourceType::kUnknown: ret_val += "Unknown"; break; @@ -98,6 +103,14 @@ class TestScenario { return opentelemetry::sdk::resource::Resource::Create(attributes); } + static opentelemetry::sdk::resource::Resource TestGceResource() { + opentelemetry::sdk::common::AttributeMap attributes; + attributes.SetAttribute("cloud.platform", "gcp_compute_engine"); + attributes.SetAttribute("cloud.availability_zone", "zone"); + attributes.SetAttribute("cloud.account.id", "id"); + return opentelemetry::sdk::resource::Resource::Create(attributes); + } + static opentelemetry::sdk::resource::Resource TestUnknownResource() { opentelemetry::sdk::common::AttributeMap attributes; attributes.SetAttribute("cloud.platform", "random"); @@ -163,6 +176,9 @@ class MetadataExchangeTest absl::get(attributes.at("csm.workload_canonical_service")), "canonical_service"); EXPECT_EQ(absl::get(attributes.at("csm.mesh_id")), "mesh-id"); + EXPECT_EQ(absl::get( + attributes.at("csm.remote_workload_canonical_service")), + "canonical_service"); if (verify_client_only_attributes) { EXPECT_EQ(absl::get(attributes.at("csm.service_name")), "unknown"); @@ -190,9 +206,20 @@ class MetadataExchangeTest EXPECT_EQ(absl::get( attributes.at("csm.remote_workload_project_id")), "id"); + break; + case TestScenario::ResourceType::kGce: + EXPECT_EQ( + absl::get(attributes.at("csm.remote_workload_type")), + "gcp_compute_engine"); + EXPECT_EQ( + absl::get(attributes.at("csm.remote_workload_name")), + "workload"); + EXPECT_EQ(absl::get( + attributes.at("csm.remote_workload_location")), + "zone"); EXPECT_EQ(absl::get( - attributes.at("csm.remote_workload_canonical_service")), - "canonical_service"); + attributes.at("csm.remote_workload_project_id")), + "id"); break; case TestScenario::ResourceType::kUnknown: EXPECT_EQ( @@ -366,6 +393,10 @@ INSTANTIATE_TEST_SUITE_P( TestScenario::XdsBootstrapSource::kFromConfig), TestScenario(TestScenario::ResourceType::kGke, TestScenario::XdsBootstrapSource::kFromFile), + TestScenario(TestScenario::ResourceType::kGce, + TestScenario::XdsBootstrapSource::kFromConfig), + TestScenario(TestScenario::ResourceType::kGce, + TestScenario::XdsBootstrapSource::kFromFile), TestScenario(TestScenario::ResourceType::kUnknown, TestScenario::XdsBootstrapSource::kFromConfig), TestScenario(TestScenario::ResourceType::kUnknown,