1 //
2 //
3 // Copyright 2024 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 "metadata_exchange.h"
20
21 #include <grpc/slice.h>
22 #include <stddef.h>
23
24 #include <algorithm>
25 #include <array>
26 #include <cstdint>
27 #include <unordered_map>
28
29 #include "absl/strings/string_view.h"
30 #include "constants.h"
31 #include "src/core/telemetry/call_tracer.h"
32
33 namespace grpc_observability {
34
PythonLabelsInjector(const std::vector<Label> & exchange_labels)35 PythonLabelsInjector::PythonLabelsInjector(
36 const std::vector<Label>& exchange_labels) {
37 for (const auto& label : exchange_labels) {
38 auto it = MetadataExchangeKeyNames.find(label.key);
39 if (it != MetadataExchangeKeyNames.end()) {
40 metadata_to_exchange_.emplace_back(label.key, label.value);
41 }
42 }
43 }
44
GetExchangeLabels(grpc_metadata_batch * incoming_initial_metadata) const45 std::vector<Label> PythonLabelsInjector::GetExchangeLabels(
46 grpc_metadata_batch* incoming_initial_metadata) const {
47 std::vector<Label> labels;
48 for (const auto& key : MetadataExchangeKeyNames) {
49 if (key == kXEnvoyPeerMetadata) {
50 auto xds_peer_metadata =
51 incoming_initial_metadata->Take(grpc_core::XEnvoyPeerMetadata());
52 grpc_core::Slice xds_remote_metadata = xds_peer_metadata.has_value()
53 ? *std::move(xds_peer_metadata)
54 : grpc_core::Slice();
55 if (!xds_remote_metadata.empty()) {
56 std::string xds_decoded_metadata;
57 bool metadata_decoded = absl::Base64Unescape(
58 xds_remote_metadata.as_string_view(), &xds_decoded_metadata);
59 if (metadata_decoded) {
60 labels.emplace_back(kXEnvoyPeerMetadata, xds_decoded_metadata);
61 }
62 }
63 }
64 }
65 return labels;
66 }
67
AddExchangeLabelsToMetadata(grpc_metadata_batch * outgoing_initial_metadata) const68 void PythonLabelsInjector::AddExchangeLabelsToMetadata(
69 grpc_metadata_batch* outgoing_initial_metadata) const {
70 for (const auto& metadata : metadata_to_exchange_) {
71 if (metadata.first == kXEnvoyPeerMetadata) {
72 grpc_core::Slice metadata_slice = grpc_core::Slice::FromCopiedString(
73 absl::Base64Escape(absl::string_view(metadata.second)));
74 outgoing_initial_metadata->Set(grpc_core::XEnvoyPeerMetadata(),
75 metadata_slice.Ref());
76 }
77 }
78 }
79
AddXdsOptionalLabels(bool is_client,absl::Span<const grpc_core::RefCountedStringValue> optional_labels_span,std::vector<Label> & labels)80 void PythonLabelsInjector::AddXdsOptionalLabels(
81 bool is_client,
82 absl::Span<const grpc_core::RefCountedStringValue> optional_labels_span,
83 std::vector<Label>& labels) {
84 if (!is_client) {
85 // Currently the CSM optional labels are only set on client.
86 return;
87 }
88 // Performs JSON label name format to CSM Observability Metric spec format
89 // conversion.
90 absl::string_view service_name =
91 optional_labels_span[static_cast<size_t>(
92 grpc_core::ClientCallTracer::CallAttemptTracer::
93 OptionalLabelKey::kXdsServiceName)]
94 .as_string_view();
95 absl::string_view service_namespace =
96 optional_labels_span[static_cast<size_t>(
97 grpc_core::ClientCallTracer::CallAttemptTracer::
98 OptionalLabelKey::kXdsServiceNamespace)]
99 .as_string_view();
100 // According to the CSM Observability Metric spec, if the control plane fails
101 // to provide these labels, the client will set their values to "unknown".
102 if (service_name.empty()) {
103 service_name = "unknown";
104 }
105 if (service_namespace.empty()) {
106 service_namespace = "unknown";
107 }
108 labels.emplace_back("csm.service_name", std::string(service_name));
109 labels.emplace_back("csm.service_namespace_name",
110 std::string(service_namespace));
111 }
112
113 } // namespace grpc_observability
114