[CSM] Add support for GCE resources (#35371)

Add support for GCE resources in CSM Observability.

Additionally, fix a bug where we were not adding the remote workload's canonical service label for unknown resource types.

Also, if zone and region are both specified, zone takes precedence.

Closes #35371

COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/35371 from yashykt:GceSupportToCsm e3064d8c3c
PiperOrigin-RevId: 597989825
pull/34086/head^2
Yash Tibrewal 1 year ago committed by Copybara-Service
parent 9f6789e2b2
commit 8231340aef
  1. 130
      src/cpp/ext/csm/metadata_exchange.cc
  2. 37
      test/cpp/ext/csm/metadata_exchange_test.cc

@ -89,8 +89,9 @@ constexpr absl::string_view kPeerCanonicalServiceAttribute =
"csm.remote_workload_canonical_service"; "csm.remote_workload_canonical_service";
// Type values used by Google Cloud Resource Detector // Type values used by Google Cloud Resource Detector
constexpr absl::string_view kGkeType = "gcp_kubernetes_engine"; 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 // A minimal class for helping with the information we need from the xDS
// bootstrap file for GSM Observability reasons. // bootstrap file for GSM Observability reasons.
@ -151,6 +152,8 @@ std::string GetXdsBootstrapContents() {
GcpResourceType StringToGcpResourceType(absl::string_view type) { GcpResourceType StringToGcpResourceType(absl::string_view type) {
if (type == kGkeType) { if (type == kGkeType) {
return GcpResourceType::kGke; return GcpResourceType::kGke;
} else if (type == kGceType) {
return GcpResourceType::kGce;
} }
return GcpResourceType::kUnknown; return GcpResourceType::kUnknown;
} }
@ -219,39 +222,19 @@ class MeshLabelsIterable : public LabelsIterable {
if (pos_ < local_labels_size) { if (pos_ < local_labels_size) {
return local_labels_[pos_++]; return local_labels_[pos_++];
} }
if (++pos_ == local_labels_size + 1) { const size_t fixed_attribute_end =
return std::make_pair(kPeerTypeAttribute, local_labels_size + kFixedAttributes.size();
GetStringValueFromUpbStruct( if (pos_ < fixed_attribute_end) {
struct_pb.struct_pb, kMetadataExchangeTypeKey, return NextFromAttributeList(struct_pb, kFixedAttributes,
struct_pb.arena.ptr())); local_labels_size);
}
// 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;
} }
return NextFromAttributeList(struct_pb, GetAttributesForType(remote_type_),
fixed_attribute_end);
} }
size_t Size() const override { size_t Size() const override {
auto& struct_pb = GetDecodedMetadata(); return local_labels_.size() + kFixedAttributes.size() +
if (struct_pb.struct_pb == nullptr) { GetAttributesForType(remote_type_).size();
return local_labels_.size();
}
if (remote_type_ != GcpResourceType::kGke) {
return local_labels_.size() + 1;
}
return local_labels_.size() + kGkeAttributeList.size() + 1;
} }
void ResetIteratorPosition() override { pos_ = 0; } void ResetIteratorPosition() override { pos_ = 0; }
@ -263,7 +246,7 @@ class MeshLabelsIterable : public LabelsIterable {
} }
private: private:
struct GkeAttribute { struct RemoteAttribute {
absl::string_view otel_attribute; absl::string_view otel_attribute;
absl::string_view metadata_attribute; absl::string_view metadata_attribute;
}; };
@ -273,18 +256,56 @@ class MeshLabelsIterable : public LabelsIterable {
google_protobuf_Struct* struct_pb = nullptr; google_protobuf_Struct* struct_pb = nullptr;
}; };
static constexpr std::array<GkeAttribute, 6> kGkeAttributeList = { static constexpr std::array<RemoteAttribute, 2> kFixedAttributes = {
GkeAttribute{kPeerWorkloadNameAttribute, RemoteAttribute{kPeerTypeAttribute, kMetadataExchangeTypeKey},
RemoteAttribute{kPeerCanonicalServiceAttribute,
kMetadataExchangeCanonicalServiceKey},
};
static constexpr std::array<RemoteAttribute, 5> kGkeAttributeList = {
RemoteAttribute{kPeerWorkloadNameAttribute,
kMetadataExchangeWorkloadNameKey}, kMetadataExchangeWorkloadNameKey},
GkeAttribute{kPeerNamespaceNameAttribute, RemoteAttribute{kPeerNamespaceNameAttribute,
kMetadataExchangeNamespaceNameKey}, kMetadataExchangeNamespaceNameKey},
GkeAttribute{kPeerClusterNameAttribute, kMetadataExchangeClusterNameKey}, RemoteAttribute{kPeerClusterNameAttribute,
GkeAttribute{kPeerLocationAttribute, kMetadataExchangeLocationKey}, kMetadataExchangeClusterNameKey},
GkeAttribute{kPeerProjectIdAttribute, kMetadataExchangeProjectIdKey}, RemoteAttribute{kPeerLocationAttribute, kMetadataExchangeLocationKey},
GkeAttribute{kPeerCanonicalServiceAttribute, RemoteAttribute{kPeerProjectIdAttribute, kMetadataExchangeProjectIdKey},
kMetadataExchangeCanonicalServiceKey}, };
static constexpr std::array<RemoteAttribute, 3> kGceAttributeList = {
RemoteAttribute{kPeerWorkloadNameAttribute,
kMetadataExchangeWorkloadNameKey},
RemoteAttribute{kPeerLocationAttribute, kMetadataExchangeLocationKey},
RemoteAttribute{kPeerProjectIdAttribute, kMetadataExchangeProjectIdKey},
}; };
static absl::Span<const RemoteAttribute> GetAttributesForType(
GcpResourceType remote_type) {
switch (remote_type) {
case GcpResourceType::kGke:
return kGkeAttributeList;
case GcpResourceType::kGce:
return kGceAttributeList;
default:
return {};
}
}
absl::optional<std::pair<absl::string_view, absl::string_view>>
NextFromAttributeList(const StructPb& struct_pb,
absl::Span<const RemoteAttribute> 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 { StructPb& GetDecodedMetadata() const {
auto* slice = absl::get_if<grpc_core::Slice>(&metadata_); auto* slice = absl::get_if<grpc_core::Slice>(&metadata_);
if (slice == nullptr) { if (slice == nullptr) {
@ -319,8 +340,12 @@ class MeshLabelsIterable : public LabelsIterable {
uint32_t pos_ = 0; uint32_t pos_ = 0;
}; };
constexpr std::array<MeshLabelsIterable::GkeAttribute, 6> constexpr std::array<MeshLabelsIterable::RemoteAttribute, 2>
MeshLabelsIterable::kFixedAttributes;
constexpr std::array<MeshLabelsIterable::RemoteAttribute, 5>
MeshLabelsIterable::kGkeAttributeList; MeshLabelsIterable::kGkeAttributeList;
constexpr std::array<MeshLabelsIterable::RemoteAttribute, 3>
MeshLabelsIterable::kGceAttributeList;
} // namespace } // namespace
@ -363,13 +388,13 @@ ServiceMeshLabelsInjector::ServiceMeshLabelsInjector(
opentelemetry::sdk::resource::SemanticConventions::kK8sNamespaceName); opentelemetry::sdk::resource::SemanticConventions::kK8sNamespaceName);
absl::string_view cluster_name_value = GetStringValueFromAttributeMap( absl::string_view cluster_name_value = GetStringValueFromAttributeMap(
map, opentelemetry::sdk::resource::SemanticConventions::kK8sClusterName); 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(
map, opentelemetry::sdk::resource::SemanticConventions:: map, opentelemetry::sdk::resource::SemanticConventions::
kCloudAvailabilityZone); // if zonal kCloudAvailabilityZone); // if zonal
if (location_value == "unknown") {
location_value = GetStringValueFromAttributeMap(
map, opentelemetry::sdk::resource::SemanticConventions::
kCloudRegion); // if regional
} }
absl::string_view project_id_value = GetStringValueFromAttributeMap( absl::string_view project_id_value = GetStringValueFromAttributeMap(
map, opentelemetry::sdk::resource::SemanticConventions::kCloudAccountId); map, opentelemetry::sdk::resource::SemanticConventions::kCloudAccountId);
@ -378,7 +403,8 @@ ServiceMeshLabelsInjector::ServiceMeshLabelsInjector(
// Create metadata to be sent over wire. // Create metadata to be sent over wire.
AddStringKeyValueToStructProto(metadata, kMetadataExchangeTypeKey, type_value, AddStringKeyValueToStructProto(metadata, kMetadataExchangeTypeKey, type_value,
arena.ptr()); arena.ptr());
// Only handle GKE for now AddStringKeyValueToStructProto(metadata, kMetadataExchangeCanonicalServiceKey,
canonical_service_value, arena.ptr());
if (type_value == kGkeType) { if (type_value == kGkeType) {
AddStringKeyValueToStructProto(metadata, kMetadataExchangeWorkloadNameKey, AddStringKeyValueToStructProto(metadata, kMetadataExchangeWorkloadNameKey,
workload_name_value, arena.ptr()); workload_name_value, arena.ptr());
@ -387,12 +413,16 @@ ServiceMeshLabelsInjector::ServiceMeshLabelsInjector(
AddStringKeyValueToStructProto(metadata, kMetadataExchangeClusterNameKey, AddStringKeyValueToStructProto(metadata, kMetadataExchangeClusterNameKey,
cluster_name_value, arena.ptr()); cluster_name_value, arena.ptr());
AddStringKeyValueToStructProto(metadata, kMetadataExchangeLocationKey, 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, AddStringKeyValueToStructProto(metadata, kMetadataExchangeProjectIdKey,
project_id_value, arena.ptr()); project_id_value, arena.ptr());
AddStringKeyValueToStructProto(metadata,
kMetadataExchangeCanonicalServiceKey,
canonical_service_value, arena.ptr());
} }
size_t output_length; size_t output_length;

@ -45,7 +45,7 @@ namespace {
class TestScenario { class TestScenario {
public: 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 }; enum class XdsBootstrapSource : std::uint8_t { kFromFile, kFromConfig };
explicit TestScenario(ResourceType type, XdsBootstrapSource bootstrap_source) explicit TestScenario(ResourceType type, XdsBootstrapSource bootstrap_source)
@ -55,6 +55,8 @@ class TestScenario {
switch (type_) { switch (type_) {
case ResourceType::kGke: case ResourceType::kGke:
return TestGkeResource(); return TestGkeResource();
case ResourceType::kGce:
return TestGceResource();
case ResourceType::kUnknown: case ResourceType::kUnknown:
return TestUnknownResource(); return TestUnknownResource();
} }
@ -66,6 +68,9 @@ class TestScenario {
case ResourceType::kGke: case ResourceType::kGke:
ret_val += "Gke"; ret_val += "Gke";
break; break;
case ResourceType::kGce:
ret_val += "Gce";
break;
case ResourceType::kUnknown: case ResourceType::kUnknown:
ret_val += "Unknown"; ret_val += "Unknown";
break; break;
@ -98,6 +103,14 @@ class TestScenario {
return opentelemetry::sdk::resource::Resource::Create(attributes); 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() { static opentelemetry::sdk::resource::Resource TestUnknownResource() {
opentelemetry::sdk::common::AttributeMap attributes; opentelemetry::sdk::common::AttributeMap attributes;
attributes.SetAttribute("cloud.platform", "random"); attributes.SetAttribute("cloud.platform", "random");
@ -163,6 +176,9 @@ class MetadataExchangeTest
absl::get<std::string>(attributes.at("csm.workload_canonical_service")), absl::get<std::string>(attributes.at("csm.workload_canonical_service")),
"canonical_service"); "canonical_service");
EXPECT_EQ(absl::get<std::string>(attributes.at("csm.mesh_id")), "mesh-id"); EXPECT_EQ(absl::get<std::string>(attributes.at("csm.mesh_id")), "mesh-id");
EXPECT_EQ(absl::get<std::string>(
attributes.at("csm.remote_workload_canonical_service")),
"canonical_service");
if (verify_client_only_attributes) { if (verify_client_only_attributes) {
EXPECT_EQ(absl::get<std::string>(attributes.at("csm.service_name")), EXPECT_EQ(absl::get<std::string>(attributes.at("csm.service_name")),
"unknown"); "unknown");
@ -190,9 +206,20 @@ class MetadataExchangeTest
EXPECT_EQ(absl::get<std::string>( EXPECT_EQ(absl::get<std::string>(
attributes.at("csm.remote_workload_project_id")), attributes.at("csm.remote_workload_project_id")),
"id"); "id");
break;
case TestScenario::ResourceType::kGce:
EXPECT_EQ(
absl::get<std::string>(attributes.at("csm.remote_workload_type")),
"gcp_compute_engine");
EXPECT_EQ(
absl::get<std::string>(attributes.at("csm.remote_workload_name")),
"workload");
EXPECT_EQ(absl::get<std::string>( EXPECT_EQ(absl::get<std::string>(
attributes.at("csm.remote_workload_canonical_service")), attributes.at("csm.remote_workload_location")),
"canonical_service"); "zone");
EXPECT_EQ(absl::get<std::string>(
attributes.at("csm.remote_workload_project_id")),
"id");
break; break;
case TestScenario::ResourceType::kUnknown: case TestScenario::ResourceType::kUnknown:
EXPECT_EQ( EXPECT_EQ(
@ -366,6 +393,10 @@ INSTANTIATE_TEST_SUITE_P(
TestScenario::XdsBootstrapSource::kFromConfig), TestScenario::XdsBootstrapSource::kFromConfig),
TestScenario(TestScenario::ResourceType::kGke, TestScenario(TestScenario::ResourceType::kGke,
TestScenario::XdsBootstrapSource::kFromFile), TestScenario::XdsBootstrapSource::kFromFile),
TestScenario(TestScenario::ResourceType::kGce,
TestScenario::XdsBootstrapSource::kFromConfig),
TestScenario(TestScenario::ResourceType::kGce,
TestScenario::XdsBootstrapSource::kFromFile),
TestScenario(TestScenario::ResourceType::kUnknown, TestScenario(TestScenario::ResourceType::kUnknown,
TestScenario::XdsBootstrapSource::kFromConfig), TestScenario::XdsBootstrapSource::kFromConfig),
TestScenario(TestScenario::ResourceType::kUnknown, TestScenario(TestScenario::ResourceType::kUnknown,

Loading…
Cancel
Save