diff --git a/BUILD b/BUILD index afec159fdba..d4b280d5a1e 100644 --- a/BUILD +++ b/BUILD @@ -30,18 +30,10 @@ config_setting( }, ) -ISTIO_EXTENSIONS = [ - "//source/extensions/common/workload_discovery:api_lib", # Experimental: WIP - "//source/extensions/filters/http/alpn:config_lib", - "//source/extensions/filters/http/istio_stats", - "//source/extensions/filters/http/peer_metadata:filter_lib", - "//source/extensions/filters/network/metadata_exchange:config_lib", -] - envoy_cc_binary( name = "envoy", repository = "@envoy", - deps = ISTIO_EXTENSIONS + [ + deps = [ "@envoy//source/exe:envoy_main_entry_lib", ], ) diff --git a/Makefile.core.mk b/Makefile.core.mk index a8d71344ac5..907ffef7409 100644 --- a/Makefile.core.mk +++ b/Makefile.core.mk @@ -18,8 +18,6 @@ SHELL := /bin/bash BAZEL_STARTUP_ARGS ?= BAZEL_BUILD_ARGS ?= BAZEL_TARGETS ?= //... -# Don't build Debian packages and Docker images in tests. -BAZEL_TEST_TARGETS ?= ${BAZEL_TARGETS} E2E_TEST_TARGETS ?= $$(go list ./...) E2E_TEST_FLAGS := -p=1 -parallel=1 HUB ?= @@ -73,10 +71,6 @@ check_wasm: clean: @bazel clean -.PHONY: gen-extensions-doc -gen-extensions-doc: - buf generate --path source/extensions/filters - gen: @scripts/gen-testdata.sh @@ -84,10 +78,7 @@ gen-check: @scripts/gen-testdata.sh -c test: - bazel $(BAZEL_STARTUP_ARGS) build $(BAZEL_BUILD_ARGS) $(BAZEL_CONFIG_CURRENT) -- $(TEST_ENVOY_TARGET) $(BAZEL_TEST_TARGETS) - if [ -n "$(BAZEL_TEST_TARGETS)" ]; then \ - bazel $(BAZEL_STARTUP_ARGS) test $(BAZEL_BUILD_ARGS) $(BAZEL_CONFIG_CURRENT) -- $(BAZEL_TEST_TARGETS); \ - fi + bazel $(BAZEL_STARTUP_ARGS) build $(BAZEL_BUILD_ARGS) $(BAZEL_CONFIG_CURRENT) -- $(TEST_ENVOY_TARGET) if [ -n "$(E2E_TEST_TARGETS)" ]; then \ env ENVOY_DEBUG=$(TEST_ENVOY_DEBUG) ENVOY_PATH=$(shell bazel $(BAZEL_STARTUP_ARGS) info $(BAZEL_BUILD_ARGS) $(BAZEL_CONFIG_CURRENT) bazel-bin)/envoy $(E2E_TEST_ENVS) GO111MODULE=on go test -timeout 30m $(E2E_TEST_FLAGS) $(E2E_TEST_TARGETS); \ fi @@ -105,7 +96,7 @@ check: @echo >&2 "Please use \"make lint\" instead." @false -lint: lint-copyright-banner format-go lint-go tidy-go lint-scripts gen-extensions-doc +lint: lint-copyright-banner format-go lint-go tidy-go lint-scripts @scripts/check-repository.sh @scripts/check-style.sh diff --git a/bazel/extension_config/extensions_build_config.bzl b/bazel/extension_config/extensions_build_config.bzl index ec84df16363..21d841bd92f 100644 --- a/bazel/extension_config/extensions_build_config.bzl +++ b/bazel/extension_config/extensions_build_config.bzl @@ -463,6 +463,9 @@ ENVOY_CONTRIB_EXTENSIONS = { "envoy.filters.http.golang": "//contrib/golang/filters/http/source:config", "envoy.filters.http.squash": "//contrib/squash/filters/http/source:config", "envoy.filters.http.sxg": "//contrib/sxg/filters/http/source:config", + "envoy.filters.http.peer_metadata": "//contrib/istio/filters/http/peer_metadata/source:config", + "envoy.filters.http.istio_stats": "//contrib/istio/filters/http/istio_stats/source:istio_stats", + "envoy.filters.http.alpn": "//contrib/istio/filters/http/alpn/source:config_lib", # # Network filters @@ -475,6 +478,7 @@ ENVOY_CONTRIB_EXTENSIONS = { "envoy.filters.network.mysql_proxy": "//contrib/mysql_proxy/filters/network/source:config", "envoy.filters.network.postgres_proxy": "//contrib/postgres_proxy/filters/network/source:config", "envoy.filters.network.rocketmq_proxy": "//contrib/rocketmq_proxy/filters/network/source:config", + "envoy.filters.network.metadata_exchange": "//contrib/istio/filters/network/metadata_exchange/source:config", # # Sip proxy @@ -527,6 +531,11 @@ ISTIO_ENABLED_CONTRIB_EXTENSIONS = [ "envoy.network.connection_balance.dlb", "envoy.load_balancing_policies.peak_ewma", "envoy.filters.http.peak_ewma", + # Extensions that contributed by Istio. + "envoy.filters.network.metadata_exchange", + "envoy.filters.http.alpn", + "envoy.filters.http.peer_metadata", + "envoy.filters.http.istio_stats", ] EXTENSIONS = dict([(k,v) for k,v in ENVOY_EXTENSIONS.items() if not k in ISTIO_DISABLED_EXTENSIONS] + diff --git a/extensions/common/BUILD b/extensions/common/BUILD deleted file mode 100644 index 2bc1771291d..00000000000 --- a/extensions/common/BUILD +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2019 Istio Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ -# - -load( - "@envoy//bazel:envoy_build_system.bzl", - "envoy_cc_library", - "envoy_cc_test", -) - -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -envoy_cc_library( - name = "metadata_object_lib", - srcs = ["metadata_object.cc"], - hdrs = ["metadata_object.h"], - repository = "@envoy", - deps = [ - "@com_google_absl//absl/strings", - "@com_google_absl//absl/types:optional", - "@envoy//envoy/common:hashable_interface", - "@envoy//envoy/registry", - "@envoy//envoy/stream_info:filter_state_interface", - "@envoy//source/common/common:hash_lib", - ], -) - -envoy_cc_test( - name = "metadata_object_test", - srcs = ["metadata_object_test.cc"], - repository = "@envoy", - deps = [ - ":metadata_object_lib", - "@envoy//envoy/registry", - ], -) diff --git a/extensions/common/metadata_object.cc b/extensions/common/metadata_object.cc deleted file mode 100644 index e5f763b2332..00000000000 --- a/extensions/common/metadata_object.cc +++ /dev/null @@ -1,376 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "extensions/common/metadata_object.h" - -#include "envoy/registry/registry.h" -#include "source/common/common/hash.h" -#include "source/common/protobuf/utility.h" - -#include "absl/strings/str_join.h" - -namespace Istio { -namespace Common { - -namespace { -static absl::flat_hash_map ALL_BAGGAGE_TOKENS = { - {NamespaceNameToken, BaggageToken::NamespaceName}, - {ClusterNameToken, BaggageToken::ClusterName}, - {ServiceNameToken, BaggageToken::ServiceName}, - {ServiceVersionToken, BaggageToken::ServiceVersion}, - {AppNameToken, BaggageToken::AppName}, - {AppVersionToken, BaggageToken::AppVersion}, - {WorkloadNameToken, BaggageToken::WorkloadName}, - {WorkloadTypeToken, BaggageToken::WorkloadType}, - {InstanceNameToken, BaggageToken::InstanceName}, -}; - -static absl::flat_hash_map ALL_WORKLOAD_TOKENS = { - {PodSuffix, WorkloadType::Pod}, - {DeploymentSuffix, WorkloadType::Deployment}, - {JobSuffix, WorkloadType::Job}, - {CronJobSuffix, WorkloadType::CronJob}, -}; - -absl::optional toSuffix(WorkloadType workload_type) { - switch (workload_type) { - case WorkloadType::Deployment: - return DeploymentSuffix; - case WorkloadType::CronJob: - return CronJobSuffix; - case WorkloadType::Job: - return JobSuffix; - case WorkloadType::Pod: - return PodSuffix; - case WorkloadType::Unknown: - default: - return {}; - } -} - -} // namespace - -Envoy::ProtobufTypes::MessagePtr WorkloadMetadataObject::serializeAsProto() const { - auto message = std::make_unique(); - const auto suffix = toSuffix(workload_type_); - if (suffix) { - (*message->mutable_fields())[WorkloadTypeToken].set_string_value(*suffix); - } - if (!workload_name_.empty()) { - (*message->mutable_fields())[WorkloadNameToken].set_string_value(workload_name_); - } - if (!cluster_name_.empty()) { - (*message->mutable_fields())[InstanceNameToken].set_string_value(instance_name_); - } - if (!cluster_name_.empty()) { - (*message->mutable_fields())[ClusterNameToken].set_string_value(cluster_name_); - } - if (!namespace_name_.empty()) { - (*message->mutable_fields())[NamespaceNameToken].set_string_value(namespace_name_); - } - if (!canonical_name_.empty()) { - (*message->mutable_fields())[ServiceNameToken].set_string_value(canonical_name_); - } - if (!canonical_revision_.empty()) { - (*message->mutable_fields())[ServiceVersionToken].set_string_value(canonical_revision_); - } - if (!app_name_.empty()) { - (*message->mutable_fields())[AppNameToken].set_string_value(app_name_); - } - if (!app_version_.empty()) { - (*message->mutable_fields())[AppVersionToken].set_string_value(app_version_); - } - if (!identity_.empty()) { - (*message->mutable_fields())[IdentityToken].set_string_value(identity_); - } - - if (!labels_.empty()) { - auto* labels = (*message->mutable_fields())[LabelsToken].mutable_struct_value(); - for (const auto& l : labels_) { - (*labels->mutable_fields())[std::string(l.first)].set_string_value(std::string(l.second)); - } - } - - return message; -} - -std::vector> -WorkloadMetadataObject::serializeAsPairs() const { - std::vector> parts; - const auto suffix = toSuffix(workload_type_); - if (suffix) { - parts.push_back({WorkloadTypeToken, *suffix}); - } - if (!workload_name_.empty()) { - parts.push_back({WorkloadNameToken, workload_name_}); - } - if (!instance_name_.empty()) { - parts.push_back({InstanceNameToken, instance_name_}); - } - if (!cluster_name_.empty()) { - parts.push_back({ClusterNameToken, cluster_name_}); - } - if (!namespace_name_.empty()) { - parts.push_back({NamespaceNameToken, namespace_name_}); - } - if (!canonical_name_.empty()) { - parts.push_back({ServiceNameToken, canonical_name_}); - } - if (!canonical_revision_.empty()) { - parts.push_back({ServiceVersionToken, canonical_revision_}); - } - if (!app_name_.empty()) { - parts.push_back({AppNameToken, app_name_}); - } - if (!app_version_.empty()) { - parts.push_back({AppVersionToken, app_version_}); - } - if (!labels_.empty()) { - for (const auto& l : labels_) { - parts.push_back({absl::StrCat("labels[]", l.first), absl::string_view(l.second)}); - } - } - return parts; -} - -absl::optional WorkloadMetadataObject::serializeAsString() const { - const auto parts = serializeAsPairs(); - return absl::StrJoin(parts, ",", absl::PairFormatter("=")); -} - -absl::optional WorkloadMetadataObject::hash() const { - return Envoy::HashUtil::xxHash64(*serializeAsString()); -} - -absl::optional WorkloadMetadataObject::owner() const { - const auto suffix = toSuffix(workload_type_); - if (suffix) { - return absl::StrCat(OwnerPrefix, namespace_name_, "/", *suffix, "s/", workload_name_); - } - return {}; -} - -WorkloadType fromSuffix(absl::string_view suffix) { - const auto it = ALL_WORKLOAD_TOKENS.find(suffix); - if (it != ALL_WORKLOAD_TOKENS.end()) { - return it->second; - } - return WorkloadType::Unknown; -} - -WorkloadType parseOwner(absl::string_view owner, absl::string_view workload) { - // Strip "s/workload_name" and check for workload type. - if (owner.size() > workload.size() + 2) { - owner.remove_suffix(workload.size() + 2); - size_t last = owner.rfind('/'); - if (last != absl::string_view::npos) { - return fromSuffix(owner.substr(last + 1)); - } - } - return WorkloadType::Unknown; -} - -google::protobuf::Struct convertWorkloadMetadataToStruct(const WorkloadMetadataObject& obj) { - google::protobuf::Struct metadata; - if (!obj.instance_name_.empty()) { - (*metadata.mutable_fields())[InstanceMetadataField].set_string_value(obj.instance_name_); - } - if (!obj.namespace_name_.empty()) { - (*metadata.mutable_fields())[NamespaceMetadataField].set_string_value(obj.namespace_name_); - } - if (!obj.workload_name_.empty()) { - (*metadata.mutable_fields())[WorkloadMetadataField].set_string_value(obj.workload_name_); - } - if (!obj.cluster_name_.empty()) { - (*metadata.mutable_fields())[ClusterMetadataField].set_string_value(obj.cluster_name_); - } - auto* labels = (*metadata.mutable_fields())[LabelsMetadataField].mutable_struct_value(); - if (!obj.canonical_name_.empty()) { - (*labels->mutable_fields())[CanonicalNameLabel].set_string_value(obj.canonical_name_); - } - if (!obj.canonical_revision_.empty()) { - (*labels->mutable_fields())[CanonicalRevisionLabel].set_string_value(obj.canonical_revision_); - } - if (!obj.app_name_.empty()) { - (*labels->mutable_fields())[AppNameLabel].set_string_value(obj.app_name_); - } - if (!obj.app_version_.empty()) { - (*labels->mutable_fields())[AppVersionLabel].set_string_value(obj.app_version_); - } - if (!obj.getLabels().empty()) { - for (const auto& lbl : obj.getLabels()) { - (*labels->mutable_fields())[std::string(lbl.first)].set_string_value(std::string(lbl.second)); - } - } - if (const auto owner = obj.owner(); owner.has_value()) { - (*metadata.mutable_fields())[OwnerMetadataField].set_string_value(*owner); - } - return metadata; -} - -// Convert struct to a metadata object. -std::unique_ptr -convertStructToWorkloadMetadata(const google::protobuf::Struct& metadata) { - return convertStructToWorkloadMetadata(metadata, {}); -} - -std::unique_ptr -convertStructToWorkloadMetadata(const google::protobuf::Struct& metadata, - const absl::flat_hash_set& additional_labels) { - absl::string_view instance, namespace_name, owner, workload, cluster, canonical_name, - canonical_revision, app_name, app_version; - std::vector> labels; - for (const auto& it : metadata.fields()) { - if (it.first == InstanceMetadataField) { - instance = it.second.string_value(); - } else if (it.first == NamespaceMetadataField) { - namespace_name = it.second.string_value(); - } else if (it.first == OwnerMetadataField) { - owner = it.second.string_value(); - } else if (it.first == WorkloadMetadataField) { - workload = it.second.string_value(); - } else if (it.first == ClusterMetadataField) { - cluster = it.second.string_value(); - } else if (it.first == LabelsMetadataField) { - for (const auto& labels_it : it.second.struct_value().fields()) { - if (labels_it.first == CanonicalNameLabel) { - canonical_name = labels_it.second.string_value(); - } else if (labels_it.first == CanonicalRevisionLabel) { - canonical_revision = labels_it.second.string_value(); - } else if (labels_it.first == AppNameLabel) { - app_name = labels_it.second.string_value(); - } else if (labels_it.first == AppVersionLabel) { - app_version = labels_it.second.string_value(); - } else if (!additional_labels.empty() && - additional_labels.contains(std::string(labels_it.first))) { - labels.push_back( - {std::string(labels_it.first), std::string(labels_it.second.string_value())}); - } - } - } - } - auto obj = std::make_unique(instance, cluster, namespace_name, workload, - canonical_name, canonical_revision, app_name, - app_version, parseOwner(owner, workload), ""); - obj->setLabels(labels); - return obj; -} - -absl::optional -convertEndpointMetadata(const std::string& endpoint_encoding) { - std::vector parts = absl::StrSplit(endpoint_encoding, ';'); - if (parts.size() < 5) { - return {}; - } - return absl::make_optional("", parts[4], parts[1], parts[0], parts[2], - parts[3], "", "", WorkloadType::Unknown, ""); -} - -std::string serializeToStringDeterministic(const google::protobuf::Struct& metadata) { - std::string out; - { - google::protobuf::io::StringOutputStream md(&out); - google::protobuf::io::CodedOutputStream mcs(&md); - mcs.SetSerializationDeterministic(true); - if (!metadata.SerializeToCodedStream(&mcs)) { - out.clear(); - } - } - return out; -} - -WorkloadMetadataObject::FieldType -WorkloadMetadataObject::getField(absl::string_view field_name) const { - const auto it = ALL_BAGGAGE_TOKENS.find(field_name); - if (it != ALL_BAGGAGE_TOKENS.end()) { - switch (it->second) { - case BaggageToken::NamespaceName: - return namespace_name_; - case BaggageToken::ClusterName: - return cluster_name_; - case BaggageToken::ServiceName: - return canonical_name_; - case BaggageToken::ServiceVersion: - return canonical_revision_; - case BaggageToken::AppName: - return app_name_; - case BaggageToken::AppVersion: - return app_version_; - case BaggageToken::WorkloadName: - return workload_name_; - case BaggageToken::WorkloadType: - if (const auto value = toSuffix(workload_type_); value.has_value()) { - return *value; - } - return "unknown"; - case BaggageToken::InstanceName: - return instance_name_; - } - } - return {}; -} - -std::unique_ptr convertBaggageToWorkloadMetadata(absl::string_view data) { - absl::string_view instance; - absl::string_view cluster; - absl::string_view workload; - absl::string_view namespace_name; - absl::string_view canonical_name; - absl::string_view canonical_revision; - absl::string_view app_name; - absl::string_view app_version; - WorkloadType workload_type = WorkloadType::Unknown; - std::vector properties = absl::StrSplit(data, ','); - for (absl::string_view property : properties) { - std::pair parts = absl::StrSplit(property, '='); - const auto it = ALL_BAGGAGE_TOKENS.find(parts.first); - if (it != ALL_BAGGAGE_TOKENS.end()) { - switch (it->second) { - case BaggageToken::NamespaceName: - namespace_name = parts.second; - break; - case BaggageToken::ClusterName: - cluster = parts.second; - break; - case BaggageToken::ServiceName: - canonical_name = parts.second; - break; - case BaggageToken::ServiceVersion: - canonical_revision = parts.second; - break; - case BaggageToken::AppName: - app_name = parts.second; - break; - case BaggageToken::AppVersion: - app_version = parts.second; - break; - case BaggageToken::WorkloadName: - workload = parts.second; - break; - case BaggageToken::WorkloadType: - workload_type = fromSuffix(parts.second); - break; - case BaggageToken::InstanceName: - instance = parts.second; - break; - } - } - } - return std::make_unique(instance, cluster, namespace_name, workload, - canonical_name, canonical_revision, app_name, - app_version, workload_type, ""); -} - -} // namespace Common -} // namespace Istio diff --git a/extensions/common/metadata_object.h b/extensions/common/metadata_object.h deleted file mode 100644 index 2a0e65a8e34..00000000000 --- a/extensions/common/metadata_object.h +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include "envoy/common/hashable.h" -#include "envoy/stream_info/filter_state.h" - -#include "source/common/protobuf/protobuf.h" - -#include "absl/types/optional.h" - -#include "google/protobuf/struct.pb.h" - -namespace Istio { -namespace Common { - -// Filter state key to store the peer metadata under. -constexpr absl::string_view DownstreamPeer = "downstream_peer"; -constexpr absl::string_view UpstreamPeer = "upstream_peer"; - -// Special filter state key to indicate the filter is done looking for peer metadata. -// This is used by network metadata exchange on failure. -constexpr absl::string_view NoPeer = "peer_not_found"; - -// Special labels used in the peer metadata. -constexpr absl::string_view CanonicalNameLabel = "service.istio.io/canonical-name"; -constexpr absl::string_view CanonicalRevisionLabel = "service.istio.io/canonical-revision"; -constexpr absl::string_view AppNameLabel = "app"; -constexpr absl::string_view AppVersionLabel = "version"; - -enum class WorkloadType { - Unknown, - Pod, - Deployment, - Job, - CronJob, -}; - -constexpr absl::string_view OwnerPrefix = "kubernetes://apis/apps/v1/namespaces/"; - -constexpr absl::string_view PodSuffix = "pod"; -constexpr absl::string_view DeploymentSuffix = "deployment"; -constexpr absl::string_view JobSuffix = "job"; -constexpr absl::string_view CronJobSuffix = "cronjob"; - -enum class BaggageToken { - NamespaceName, - ClusterName, - ServiceName, - ServiceVersion, - AppName, - AppVersion, - WorkloadName, - WorkloadType, - InstanceName, -}; - -constexpr absl::string_view NamespaceNameToken = "namespace"; -constexpr absl::string_view ClusterNameToken = "cluster"; -constexpr absl::string_view ServiceNameToken = "service"; -constexpr absl::string_view ServiceVersionToken = "revision"; -constexpr absl::string_view AppNameToken = "app"; -constexpr absl::string_view AppVersionToken = "version"; -constexpr absl::string_view WorkloadNameToken = "workload"; -constexpr absl::string_view WorkloadTypeToken = "type"; -constexpr absl::string_view InstanceNameToken = "name"; -constexpr absl::string_view LabelsToken = "labels"; -constexpr absl::string_view IdentityToken = "identity"; - -constexpr absl::string_view InstanceMetadataField = "NAME"; -constexpr absl::string_view NamespaceMetadataField = "NAMESPACE"; -constexpr absl::string_view ClusterMetadataField = "CLUSTER_ID"; -constexpr absl::string_view OwnerMetadataField = "OWNER"; -constexpr absl::string_view WorkloadMetadataField = "WORKLOAD_NAME"; -constexpr absl::string_view LabelsMetadataField = "LABELS"; - -class WorkloadMetadataObject : public Envoy::StreamInfo::FilterState::Object, - public Envoy::Hashable { -public: - explicit WorkloadMetadataObject(absl::string_view instance_name, absl::string_view cluster_name, - absl::string_view namespace_name, absl::string_view workload_name, - absl::string_view canonical_name, - absl::string_view canonical_revision, absl::string_view app_name, - absl::string_view app_version, WorkloadType workload_type, - absl::string_view identity) - : instance_name_(instance_name), cluster_name_(cluster_name), namespace_name_(namespace_name), - workload_name_(workload_name), canonical_name_(canonical_name), - canonical_revision_(canonical_revision), app_name_(app_name), app_version_(app_version), - workload_type_(workload_type), identity_(identity) {} - - absl::optional hash() const override; - Envoy::ProtobufTypes::MessagePtr serializeAsProto() const override; - std::vector> serializeAsPairs() const; - absl::optional serializeAsString() const override; - absl::optional owner() const; - bool hasFieldSupport() const override { return true; } - using Envoy::StreamInfo::FilterState::Object::FieldType; - FieldType getField(absl::string_view) const override; - void setLabels(std::vector> labels) { labels_ = labels; } - std::vector> getLabels() const { return labels_; } - - const std::string instance_name_; - const std::string cluster_name_; - const std::string namespace_name_; - const std::string workload_name_; - const std::string canonical_name_; - const std::string canonical_revision_; - const std::string app_name_; - const std::string app_version_; - const WorkloadType workload_type_; - const std::string identity_; - std::vector> labels_; -}; - -// Parse string workload type. -WorkloadType fromSuffix(absl::string_view suffix); - -// Parse owner field from kubernetes to detect the workload type. -WorkloadType parseOwner(absl::string_view owner, absl::string_view workload); - -// Convert a metadata object to a struct. -google::protobuf::Struct convertWorkloadMetadataToStruct(const WorkloadMetadataObject& obj); - -// Convert struct to a metadata object. -std::unique_ptr -convertStructToWorkloadMetadata(const google::protobuf::Struct& metadata); - -std::unique_ptr -convertStructToWorkloadMetadata(const google::protobuf::Struct& metadata, - const absl::flat_hash_set& additional_labels); - -// Convert endpoint metadata string to a metadata object. -// Telemetry metadata is compressed into a semicolon separated string: -// workload-name;namespace;canonical-service-name;canonical-service-revision;cluster-id. -// Telemetry metadata is stored as a string under "istio", "workload" field -// path. -absl::optional -convertEndpointMetadata(const std::string& endpoint_encoding); - -std::string serializeToStringDeterministic(const google::protobuf::Struct& metadata); - -// Convert from baggage encoding. -std::unique_ptr convertBaggageToWorkloadMetadata(absl::string_view data); - -} // namespace Common -} // namespace Istio diff --git a/extensions/common/metadata_object_test.cc b/extensions/common/metadata_object_test.cc deleted file mode 100644 index 90003b95570..00000000000 --- a/extensions/common/metadata_object_test.cc +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "extensions/common/metadata_object.h" - -#include "envoy/registry/registry.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -namespace Istio { -namespace Common { - -using Envoy::Protobuf::util::MessageDifferencer; -using ::testing::NiceMock; - -TEST(WorkloadMetadataObjectTest, Baggage) { - WorkloadMetadataObject deploy("pod-foo-1234", "my-cluster", "default", "foo", "foo-service", - "v1alpha3", "", "", WorkloadType::Deployment, ""); - - WorkloadMetadataObject pod("pod-foo-1234", "my-cluster", "default", "foo", "foo-service", - "v1alpha3", "", "", WorkloadType::Pod, ""); - - WorkloadMetadataObject cronjob("pod-foo-1234", "my-cluster", "default", "foo", "foo-service", - "v1alpha3", "foo-app", "v1", WorkloadType::CronJob, ""); - - WorkloadMetadataObject job("pod-foo-1234", "my-cluster", "default", "foo", "foo-service", - "v1alpha3", "", "", WorkloadType::Job, ""); - - EXPECT_EQ(deploy.serializeAsString(), - absl::StrCat("type=deployment,workload=foo,name=pod-foo-1234,cluster=my-cluster,", - "namespace=default,service=foo-service,revision=v1alpha3")); - - EXPECT_EQ(pod.serializeAsString(), - absl::StrCat("type=pod,workload=foo,name=pod-foo-1234,cluster=my-cluster,", - "namespace=default,service=foo-service,revision=v1alpha3")); - - EXPECT_EQ(cronjob.serializeAsString(), - absl::StrCat("type=cronjob,workload=foo,name=pod-foo-1234,cluster=my-cluster,", - "namespace=default,service=foo-service,revision=v1alpha3,", - "app=foo-app,version=v1")); - - EXPECT_EQ(job.serializeAsString(), - absl::StrCat("type=job,workload=foo,name=pod-foo-1234,cluster=my-cluster,", - "namespace=default,service=foo-service,revision=v1alpha3")); -} - -void checkStructConversion(const Envoy::StreamInfo::FilterState::Object& data) { - const auto& obj = dynamic_cast(data); - auto pb = convertWorkloadMetadataToStruct(obj); - auto obj2 = convertStructToWorkloadMetadata(pb); - EXPECT_EQ(obj2->serializeAsString(), obj.serializeAsString()); - MessageDifferencer::Equals(*(obj2->serializeAsProto()), *(obj.serializeAsProto())); - EXPECT_EQ(obj2->hash(), obj.hash()); -} - -TEST(WorkloadMetadataObjectTest, ConversionWithLabels) { - WorkloadMetadataObject deploy("pod-foo-1234", "my-cluster", "default", "foo", "foo-service", - "v1alpha3", "", "", WorkloadType::Deployment, ""); - deploy.setLabels({{"label1", "value1"}, {"label2", "value2"}}); - auto pb = convertWorkloadMetadataToStruct(deploy); - auto obj1 = convertStructToWorkloadMetadata(pb, {"label1", "label2"}); - EXPECT_EQ(obj1->getLabels().size(), 2); - auto obj2 = convertStructToWorkloadMetadata(pb, {"label1"}); - EXPECT_EQ(obj2->getLabels().size(), 1); - absl::flat_hash_set empty; - auto obj3 = convertStructToWorkloadMetadata(pb, empty); - EXPECT_EQ(obj3->getLabels().size(), 0); -} - -TEST(WorkloadMetadataObjectTest, Conversion) { - { - const auto r = convertBaggageToWorkloadMetadata( - "type=deployment,workload=foo,cluster=my-cluster," - "namespace=default,service=foo-service,revision=v1alpha3,app=foo-app,version=latest"); - EXPECT_EQ(absl::get(r->getField("service")), "foo-service"); - EXPECT_EQ(absl::get(r->getField("revision")), "v1alpha3"); - EXPECT_EQ(absl::get(r->getField("type")), DeploymentSuffix); - EXPECT_EQ(absl::get(r->getField("workload")), "foo"); - EXPECT_EQ(absl::get(r->getField("name")), ""); - EXPECT_EQ(absl::get(r->getField("namespace")), "default"); - EXPECT_EQ(absl::get(r->getField("cluster")), "my-cluster"); - EXPECT_EQ(absl::get(r->getField("app")), "foo-app"); - EXPECT_EQ(absl::get(r->getField("version")), "latest"); - checkStructConversion(*r); - } - - { - const auto r = - convertBaggageToWorkloadMetadata("type=pod,name=foo-pod-435,cluster=my-cluster,namespace=" - "test,service=foo-service,revision=v1beta2"); - EXPECT_EQ(absl::get(r->getField("service")), "foo-service"); - EXPECT_EQ(absl::get(r->getField("revision")), "v1beta2"); - EXPECT_EQ(absl::get(r->getField("type")), PodSuffix); - EXPECT_EQ(absl::get(r->getField("workload")), ""); - EXPECT_EQ(absl::get(r->getField("name")), "foo-pod-435"); - EXPECT_EQ(absl::get(r->getField("namespace")), "test"); - EXPECT_EQ(absl::get(r->getField("cluster")), "my-cluster"); - EXPECT_EQ(absl::get(r->getField("app")), ""); - EXPECT_EQ(absl::get(r->getField("version")), ""); - checkStructConversion(*r); - } - - { - const auto r = - convertBaggageToWorkloadMetadata("type=job,name=foo-job-435,cluster=my-cluster,namespace=" - "test,service=foo-service,revision=v1beta4"); - EXPECT_EQ(absl::get(r->getField("service")), "foo-service"); - EXPECT_EQ(absl::get(r->getField("revision")), "v1beta4"); - EXPECT_EQ(absl::get(r->getField("type")), JobSuffix); - EXPECT_EQ(absl::get(r->getField("workload")), ""); - EXPECT_EQ(absl::get(r->getField("name")), "foo-job-435"); - EXPECT_EQ(absl::get(r->getField("namespace")), "test"); - EXPECT_EQ(absl::get(r->getField("cluster")), "my-cluster"); - checkStructConversion(*r); - } - - { - const auto r = - convertBaggageToWorkloadMetadata("type=cronjob,workload=foo-cronjob,cluster=my-cluster," - "namespace=test,service=foo-service,revision=v1beta4"); - EXPECT_EQ(absl::get(r->getField("service")), "foo-service"); - EXPECT_EQ(absl::get(r->getField("revision")), "v1beta4"); - EXPECT_EQ(absl::get(r->getField("type")), CronJobSuffix); - EXPECT_EQ(absl::get(r->getField("workload")), "foo-cronjob"); - EXPECT_EQ(absl::get(r->getField("name")), ""); - EXPECT_EQ(absl::get(r->getField("namespace")), "test"); - EXPECT_EQ(absl::get(r->getField("cluster")), "my-cluster"); - checkStructConversion(*r); - } - - { - const auto r = convertBaggageToWorkloadMetadata( - "type=deployment,workload=foo,namespace=default,service=foo-service,revision=v1alpha3"); - EXPECT_EQ(absl::get(r->getField("service")), "foo-service"); - EXPECT_EQ(absl::get(r->getField("revision")), "v1alpha3"); - EXPECT_EQ(absl::get(r->getField("type")), DeploymentSuffix); - EXPECT_EQ(absl::get(r->getField("workload")), "foo"); - EXPECT_EQ(absl::get(r->getField("namespace")), "default"); - EXPECT_EQ(absl::get(r->getField("cluster")), ""); - checkStructConversion(*r); - } - - { - const auto r = convertBaggageToWorkloadMetadata("namespace=default"); - EXPECT_EQ(absl::get(r->getField("namespace")), "default"); - checkStructConversion(*r); - } -} - -TEST(WorkloadMetadataObjectTest, ConvertFromEmpty) { - google::protobuf::Struct node; - auto obj = convertStructToWorkloadMetadata(node); - EXPECT_EQ(obj->serializeAsString(), ""); - checkStructConversion(*obj); -} - -TEST(WorkloadMetadataObjectTest, ConvertFromEndpointMetadata) { - EXPECT_EQ(absl::nullopt, convertEndpointMetadata("")); - EXPECT_EQ(absl::nullopt, convertEndpointMetadata("a;b")); - EXPECT_EQ(absl::nullopt, convertEndpointMetadata("a;;;b")); - EXPECT_EQ(absl::nullopt, convertEndpointMetadata("a;b;c;d")); - auto obj = convertEndpointMetadata("foo-pod;default;foo-service;v1;my-cluster"); - ASSERT_TRUE(obj.has_value()); - EXPECT_EQ(obj->serializeAsString(), "workload=foo-pod,cluster=my-cluster," - "namespace=default,service=foo-service,revision=v1"); -} - -} // namespace Common -} // namespace Istio diff --git a/go.mod b/go.mod index d9c6cc373eb..e9de8d294be 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.24.0 require ( github.com/cncf/xds/go v0.0.0-20251110193048-8bfbf64dc13e github.com/envoyproxy/go-control-plane v0.14.1-0.20260103185439-d6ff64e48402 + github.com/envoyproxy/go-control-plane/contrib v1.36.1-0.20260110132102-c80c497953e7 github.com/envoyproxy/go-control-plane/envoy v1.36.1-0.20260117034604-3a80eae751cf github.com/golang/protobuf v1.5.4 github.com/google/go-cmp v0.7.0 diff --git a/go.sum b/go.sum index f6278bd8c8e..fc614ff1fdc 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.14.1-0.20260103185439-d6ff64e48402 h1:Jm3kw/Enxm3pcwPwKpjNanZSVq6N/XyA0pkcLI9BVpk= github.com/envoyproxy/go-control-plane v0.14.1-0.20260103185439-d6ff64e48402/go.mod h1:iuP4OVLgz85ISHlL+dS0cf6wg5cCz/KmuySk+g+F3uY= +github.com/envoyproxy/go-control-plane/contrib v1.36.1-0.20260110132102-c80c497953e7 h1:zvIihMJhSiXf9xhwOeKl7RO+ynIFEKSSYpab+ryEjrM= +github.com/envoyproxy/go-control-plane/contrib v1.36.1-0.20260110132102-c80c497953e7/go.mod h1:+fG/snSdlOxU+5RWuuKSYxF9zusT3Duy1MDbETA44Bo= github.com/envoyproxy/go-control-plane/envoy v1.36.1-0.20260117034604-3a80eae751cf h1:xZp0i/4HHESpsaPxpTy1am8g0JUFjw7JMR58ECOD7Qo= github.com/envoyproxy/go-control-plane/envoy v1.36.1-0.20260117034604-3a80eae751cf/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= diff --git a/source/extensions/common/workload_discovery/BUILD b/source/extensions/common/workload_discovery/BUILD deleted file mode 100644 index 13b7c90b41d..00000000000 --- a/source/extensions/common/workload_discovery/BUILD +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright Istio Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ -# - -load( - "@envoy//bazel:envoy_build_system.bzl", - "envoy_cc_library", - "envoy_proto_library", -) - -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -envoy_cc_library( - name = "api_lib", - srcs = ["api.cc"], - hdrs = ["api.h"], - repository = "@envoy", - deps = [ - ":discovery_cc_proto", - "//extensions/common:metadata_object_lib", - "@envoy//envoy/registry", - "@envoy//envoy/server:bootstrap_extension_config_interface", - "@envoy//envoy/server:factory_context_interface", - "@envoy//envoy/singleton:manager_interface", - "@envoy//envoy/stats:stats_macros", - "@envoy//envoy/thread_local:thread_local_interface", - "@envoy//source/common/common:non_copyable", - "@envoy//source/common/config:subscription_base_interface", - "@envoy//source/common/grpc:common_lib", - "@envoy//source/common/init:target_lib", - ], -) - -envoy_proto_library( - name = "discovery", - srcs = [ - "discovery.proto", - "extension.proto", - ], - deps = [ - "@envoy_api//envoy/config/core/v3:pkg", - ], -) diff --git a/source/extensions/common/workload_discovery/api.cc b/source/extensions/common/workload_discovery/api.cc deleted file mode 100644 index 86ffcd24609..00000000000 --- a/source/extensions/common/workload_discovery/api.cc +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "source/extensions/common/workload_discovery/api.h" - -#include "envoy/registry/registry.h" -#include "envoy/server/bootstrap_extension_config.h" -#include "envoy/server/factory_context.h" -#include "envoy/singleton/manager.h" -#include "envoy/thread_local/thread_local.h" -#include "source/common/common/non_copyable.h" -#include "source/common/config/subscription_base.h" -#include "source/common/grpc/common.h" -#include "source/common/init/target_impl.h" -#include "source/extensions/common/workload_discovery/discovery.pb.h" -#include "source/extensions/common/workload_discovery/discovery.pb.validate.h" -#include "source/extensions/common/workload_discovery/extension.pb.h" -#include "source/extensions/common/workload_discovery/extension.pb.validate.h" - -namespace Envoy::Extensions::Common::WorkloadDiscovery { -namespace { -constexpr absl::string_view DefaultNamespace = "default"; -constexpr absl::string_view DefaultServiceAccount = "default"; -constexpr absl::string_view DefaultTrustDomain = "cluster.local"; -Istio::Common::WorkloadMetadataObject convert(const istio::workload::Workload& workload) { - auto workload_type = Istio::Common::WorkloadType::Deployment; - switch (workload.workload_type()) { - case istio::workload::WorkloadType::CRONJOB: - workload_type = Istio::Common::WorkloadType::CronJob; - break; - case istio::workload::WorkloadType::JOB: - workload_type = Istio::Common::WorkloadType::Job; - break; - case istio::workload::WorkloadType::POD: - workload_type = Istio::Common::WorkloadType::Pod; - break; - default: - break; - } - - absl::string_view ns = workload.namespace_(); - absl::string_view trust_domain = workload.trust_domain(); - absl::string_view service_account = workload.service_account(); - // Trust domain may be elided if it's equal to "cluster.local" - if (trust_domain.empty()) { - trust_domain = DefaultTrustDomain; - } - // The namespace may be elided if it's equal to "default" - if (ns.empty()) { - ns = DefaultNamespace; - } - // The service account may be elided if it's equal to "default" - if (service_account.empty()) { - service_account = DefaultServiceAccount; - } - const auto identity = - absl::StrCat("spiffe://", trust_domain, "/ns/", ns, "/sa/", service_account); - return Istio::Common::WorkloadMetadataObject( - workload.name(), workload.cluster_id(), ns, workload.workload_name(), - workload.canonical_name(), workload.canonical_revision(), workload.canonical_name(), - workload.canonical_revision(), workload_type, identity); -} -} // namespace - -class WorkloadMetadataProviderImpl : public WorkloadMetadataProvider, public Singleton::Instance { -public: - WorkloadMetadataProviderImpl(const envoy::config::core::v3::ConfigSource& config_source, - Server::Configuration::ServerFactoryContext& factory_context) - : config_source_(config_source), factory_context_(factory_context), - tls_(factory_context.threadLocal()), - scope_(factory_context.scope().createScope("workload_discovery")), - stats_(generateStats(*scope_)), subscription_(*this) { - tls_.set([](Event::Dispatcher&) { return std::make_shared(); }); - // This is safe because the ADS mux is started in the cluster manager constructor prior to this - // call. - subscription_.start(); - } - - std::optional - GetMetadata(const Network::Address::InstanceConstSharedPtr& address) override { - if (address && address->ip()) { - if (const auto ipv4 = address->ip()->ipv4(); ipv4) { - uint32_t value = ipv4->address(); - std::array output; - absl::little_endian::Store32(&output, value); - return tls_->get(std::string(output.begin(), output.end())); - } else if (const auto ipv6 = address->ip()->ipv6(); ipv6) { - const uint64_t high = absl::Uint128High64(ipv6->address()); - const uint64_t low = absl::Uint128Low64(ipv6->address()); - std::array output; - absl::little_endian::Store64(&output, low); - absl::little_endian::Store64(&output[8], high); - return tls_->get(std::string(output.begin(), output.end())); - } - } - return {}; - } - -private: - using IdToAddress = absl::flat_hash_map>; - using IdToAddressSharedPtr = std::shared_ptr; - using AddressToWorkload = absl::flat_hash_map; - using AddressToWorkloadSharedPtr = std::shared_ptr; - - struct ThreadLocalProvider : public ThreadLocal::ThreadLocalObject { - void reset(const AddressToWorkloadSharedPtr& index) { address_to_workload_ = *index; } - void update(const AddressToWorkloadSharedPtr& added_addresses, - const IdToAddressSharedPtr& added_ids, - const std::shared_ptr> removed) { - for (const auto& id : *removed) { - for (const auto& address : id_to_address_[id]) { - address_to_workload_.erase(address); - } - id_to_address_.erase(id); - } - for (const auto& [address, workload] : *added_addresses) { - address_to_workload_.emplace(address, workload); - } - for (const auto& [id, address] : *added_ids) { - id_to_address_.emplace(id, address); - } - } - size_t total() const { return address_to_workload_.size(); } - // Returns by-value since the flat map does not provide pointer stability. - std::optional get(const std::string& address) { - const auto it = address_to_workload_.find(address); - if (it != address_to_workload_.end()) { - return it->second; - } - return {}; - } - IdToAddress id_to_address_; - AddressToWorkload address_to_workload_; - }; - class WorkloadSubscription : Config::SubscriptionBase { - public: - WorkloadSubscription(WorkloadMetadataProviderImpl& parent) - : Config::SubscriptionBase( - parent.factory_context_.messageValidationVisitor(), "uid"), - parent_(parent) { - subscription_ = THROW_OR_RETURN_VALUE( - parent.factory_context_.clusterManager() - .subscriptionFactory() - .subscriptionFromConfigSource(parent.config_source_, - Grpc::Common::typeUrl(getResourceName()), - *parent.scope_, *this, resource_decoder_, {}), - Config::SubscriptionPtr); - } - void start() { subscription_->start({}); } - - private: - // Config::SubscriptionCallbacks - absl::Status onConfigUpdate(const std::vector& resources, - const std::string&) override { - AddressToWorkloadSharedPtr index = std::make_shared(); - for (const auto& resource : resources) { - const auto& workload = - dynamic_cast(resource.get().resource()); - const auto& metadata = convert(workload); - for (const auto& addr : workload.addresses()) { - index->emplace(addr, metadata); - } - } - parent_.reset(index); - return absl::OkStatus(); - } - absl::Status onConfigUpdate(const std::vector& added_resources, - const Protobuf::RepeatedPtrField& removed_resources, - const std::string&) override { - IdToAddressSharedPtr added_ids = std::make_shared(); - AddressToWorkloadSharedPtr added_addresses = std::make_shared(); - for (const auto& resource : added_resources) { - const auto& workload = - dynamic_cast(resource.get().resource()); - const auto& metadata = convert(workload); - for (const auto& addr : workload.addresses()) { - added_addresses->emplace(addr, metadata); - } - added_ids->emplace(workload.uid(), std::vector(workload.addresses().begin(), - workload.addresses().end())); - } - auto removed = std::make_shared>(); - removed->reserve(removed_resources.size()); - for (const auto& resource : removed_resources) { - removed->push_back(resource); - } - parent_.update(added_addresses, added_ids, removed); - return absl::OkStatus(); - } - void onConfigUpdateFailed(Config::ConfigUpdateFailureReason, const EnvoyException*) override { - // Do nothing - feature is automatically disabled. - // TODO: Potential issue with the expiration of the metadata. - } - WorkloadMetadataProviderImpl& parent_; - Config::SubscriptionPtr subscription_; - }; - - void reset(AddressToWorkloadSharedPtr index) { - tls_.runOnAllThreads([index](OptRef tls) { tls->reset(index); }); - stats_.total_.set(tls_->total()); - } - - void update(const AddressToWorkloadSharedPtr& added_addresses, - const IdToAddressSharedPtr& added_ids, - const std::shared_ptr> removed) { - tls_.runOnAllThreads([added_addresses, added_ids, removed](OptRef tls) { - tls->update(added_addresses, added_ids, removed); - }); - stats_.total_.set(tls_->total()); - } - - WorkloadDiscoveryStats generateStats(Stats::Scope& scope) { - return WorkloadDiscoveryStats{WORKLOAD_DISCOVERY_STATS(POOL_GAUGE(scope))}; - } - - const envoy::config::core::v3::ConfigSource config_source_; - Server::Configuration::ServerFactoryContext& factory_context_; - ThreadLocal::TypedSlot tls_; - Stats::ScopeSharedPtr scope_; - WorkloadDiscoveryStats stats_; - WorkloadSubscription subscription_; -}; - -SINGLETON_MANAGER_REGISTRATION(workload_metadata_provider) - -class WorkloadDiscoveryExtension : public Server::BootstrapExtension { -public: - WorkloadDiscoveryExtension(Server::Configuration::ServerFactoryContext& factory_context, - const istio::workload::BootstrapExtension& config) - : factory_context_(factory_context), config_(config) {} - - // Server::Configuration::BootstrapExtension - void onServerInitialized() override { - provider_ = factory_context_.singletonManager().getTyped( - SINGLETON_MANAGER_REGISTERED_NAME(workload_metadata_provider), [&] { - return std::make_shared(config_.config_source(), - factory_context_); - }); - } - - void onWorkerThreadInitialized() override{}; - -private: - Server::Configuration::ServerFactoryContext& factory_context_; - const istio::workload::BootstrapExtension config_; - WorkloadMetadataProviderSharedPtr provider_; -}; - -class WorkloadDiscoveryFactory : public Server::Configuration::BootstrapExtensionFactory { -public: - // Server::Configuration::BootstrapExtensionFactory - Server::BootstrapExtensionPtr - createBootstrapExtension(const Protobuf::Message& config, - Server::Configuration::ServerFactoryContext& context) override { - const auto& message = - MessageUtil::downcastAndValidate( - config, context.messageValidationVisitor()); - return std::make_unique(context, message); - } - ProtobufTypes::MessagePtr createEmptyConfigProto() override { - return std::make_unique(); - } - std::string name() const override { return "envoy.bootstrap.workload_discovery"; }; -}; - -REGISTER_FACTORY(WorkloadDiscoveryFactory, Server::Configuration::BootstrapExtensionFactory); - -WorkloadMetadataProviderSharedPtr -GetProvider(Server::Configuration::ServerFactoryContext& context) { - return context.singletonManager().getTyped( - SINGLETON_MANAGER_REGISTERED_NAME(workload_metadata_provider)); -} - -} // namespace Envoy::Extensions::Common::WorkloadDiscovery diff --git a/source/extensions/common/workload_discovery/api.h b/source/extensions/common/workload_discovery/api.h deleted file mode 100644 index 38ad400c658..00000000000 --- a/source/extensions/common/workload_discovery/api.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include "envoy/network/address.h" -#include "envoy/stats/stats_macros.h" -#include "envoy/server/factory_context.h" -#include "extensions/common/metadata_object.h" - -namespace Envoy::Extensions::Common::WorkloadDiscovery { - -#define WORKLOAD_DISCOVERY_STATS(GAUGE) GAUGE(total, NeverImport) - -struct WorkloadDiscoveryStats { - WORKLOAD_DISCOVERY_STATS(GENERATE_GAUGE_STRUCT) -}; - -class WorkloadMetadataProvider { -public: - virtual ~WorkloadMetadataProvider() = default; - virtual std::optional - GetMetadata(const Network::Address::InstanceConstSharedPtr& address) PURE; -}; - -using WorkloadMetadataProviderSharedPtr = std::shared_ptr; - -WorkloadMetadataProviderSharedPtr GetProvider(Server::Configuration::ServerFactoryContext& context); - -} // namespace Envoy::Extensions::Common::WorkloadDiscovery diff --git a/source/extensions/common/workload_discovery/discovery.proto b/source/extensions/common/workload_discovery/discovery.proto deleted file mode 100644 index 128f14ab1f2..00000000000 --- a/source/extensions/common/workload_discovery/discovery.proto +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package istio.workload; -option go_package = "test/envoye2e/workloadapi"; - -/** - * Warning: Derived from - * https://github.com/istio/ztunnel/blob/e36680f1534fae3d158964500ae9185495ec5d7b/proto/workload.proto - * with the following changes: - * - * 1) change go_package; - * 2) append bootstrap extension stub; - */ - -// NetworkMode indicates how the addresses of the workload should be treated. -enum NetworkMode { - // STANDARD means that the workload is uniquely identified by its address (within its network). - STANDARD = 0; - // HOST_NETWORK means the workload has an IP address that is shared by many workloads. The data plane should avoid - // attempting to lookup these workloads by IP address (which could return the wrong result). - HOST_NETWORK = 1; -} - -// Workload represents a workload - an endpoint (or collection behind a hostname). -// The xds primary key is "uid" as defined on the workload below. -// Secondary (alias) keys are the unique `network/IP` pairs that the workload can be reached at. -message Workload { - // UID represents a globally unique opaque identifier for this workload. - // For k8s resources, it is recommended to use the more readable format: - // - // cluster/group/kind/namespace/name/section-name - // - // As an example, a ServiceEntry with two WorkloadEntries inlined could become - // two Workloads with the following UIDs: - // - cluster1/networking.istio.io/v1alpha3/ServiceEntry/default/external-svc/endpoint1 - // - cluster1/networking.istio.io/v1alpha3/ServiceEntry/default/external-svc/endpoint2 - // - // For VMs and other workloads other formats are also supported; for example, - // a single UID string: "0ae5c03d-5fb3-4eb9-9de8-2bd4b51606ba" - string uid = 20; - // Name represents the name for the workload. - // For Kubernetes, this is the pod name. - // This is just for debugging and may be elided as an optimization. - string name = 1; - // Namespace represents the namespace for the workload. - // This is just for debugging and may be elided as an optimization. - string namespace = 2; - - // Address represents the IPv4/IPv6 address for the workload. - // This should be globally unique. - // This should not have a port number. - // Each workload must have at least either an address or hostname; not both. - repeated bytes addresses = 3; - - // The hostname for the workload to be resolved by the ztunnel. - // DNS queries are sent on-demand by default. - // If the resolved DNS query has several endpoints, the request will be forwarded - // to the first response. - // - // At a minimum, each workload must have either an address or hostname. For example, - // a workload that backs a Kubernetes service will typically have only endpoints. A - // workload that backs a headless Kubernetes service, however, will have both - // addresses as well as a hostname used for direct access to the headless endpoint. - string hostname = 21; - - // Network represents the network this workload is on. This may be elided for the default network. - // A (network,address) pair makeup a unique key for a workload *at a point in time*. - string network = 4; - - // Protocol that should be used to connect to this workload. - TunnelProtocol tunnel_protocol = 5; - - // The SPIFFE identity of the workload. The identity is joined to form spiffe:///ns//sa/. - // TrustDomain of the workload. May be elided if this is the mesh wide default (typically cluster.local) - string trust_domain = 6; - // ServiceAccount of the workload. May be elided if this is "default" - string service_account = 7; - - // If present, the waypoint proxy for this workload. - // All incoming requests must go through the waypoint. - GatewayAddress waypoint = 8; - - // If present, East West network gateway this workload can be reached through. - // Requests from remote networks should traverse this gateway. - GatewayAddress network_gateway = 19; - - // Name of the node the workload runs on - string node = 9; - - // CanonicalName for the workload. Used for telemetry. - string canonical_name = 10; - // CanonicalRevision for the workload. Used for telemetry. - string canonical_revision = 11; - // WorkloadType represents the type of the workload. Used for telemetry. - WorkloadType workload_type = 12; - // WorkloadName represents the name for the workload (of type WorkloadType). Used for telemetry. - string workload_name = 13; - - // If set, this indicates a workload expects to directly receive tunnel traffic. - // In ztunnel, this means: - // * Requests *from* this workload do not need to be tunneled if they already are tunneled by the tunnel_protocol. - // * Requests *to* this workload, via the tunnel_protocol, do not need to be de-tunneled. - bool native_tunnel = 14; - - // If an application, such as a sandwiched waypoint proxy, supports directly - // receiving information from zTunnel they can set application_protocol. - ApplicationTunnel application_tunnel = 23; - - // The services for which this workload is an endpoint. - // The key is the NamespacedHostname string of the format namespace/hostname. - map services = 22; - - // A list of authorization policies applicable to this workload. - // NOTE: this *only* includes Selector based policies. Namespace and global polices - // are returned out of band. - // Authorization policies are only valid for workloads with `addresses` rather than `hostname`. - repeated string authorization_policies = 16; - - WorkloadStatus status = 17; - - // The cluster ID that the workload instance belongs to - string cluster_id = 18; - - // The Locality defines information about where a workload is geographically deployed - Locality locality = 24; - - NetworkMode network_mode = 25; - - // Reservations for deleted fields. - reserved 15; -} - -message Locality { - string region = 1; - string zone = 2; - string subzone = 3; -} - -enum WorkloadStatus { - // Workload is healthy and ready to serve traffic. - HEALTHY = 0; - // Workload is unhealthy and NOT ready to serve traffic. - UNHEALTHY = 1; -} - -enum WorkloadType { - DEPLOYMENT = 0; - CRONJOB = 1; - POD = 2; - JOB = 3; -} - -// PorList represents the ports for a service -message PortList { - repeated Port ports = 1; -} - -message Port { - // Port the service is reached at (frontend). - uint32 service_port = 1; - // Port the service forwards to (backend). - uint32 target_port = 2; -} - -// TunnelProtocol indicates the tunneling protocol for requests. -enum TunnelProtocol { - // NONE means requests should be forwarded as-is, without tunneling. - NONE = 0; - // HBONE means requests should be tunneled over HTTP. - // This does not dictate HTTP/1.1 vs HTTP/2; ALPN should be used for that purpose. - HBONE = 1; - // Future options may include things like QUIC/HTTP3, etc. -} - -// ApplicationProtocol specifies a workload (application or gateway) can -// consume tunnel information. -message ApplicationTunnel { - enum Protocol { - // Bytes are copied from the inner stream without modification. - NONE = 0; - - // Prepend PROXY protocol headers before copying bytes - // Standard PROXY source and destination information - // is included, along with potential extra TLV headers: - // 0xD0 - The SPIFFE identity of the source workload - // 0xD1 - The FQDN or Hostname of the targeted Service - PROXY = 1; - } - - // A target natively handles this type of traffic. - Protocol protocol = 1; - - // optional: if set, traffic should be sent to this port after the last zTunnel hop - uint32 port = 2; -} - -// GatewayAddress represents the address of a gateway -message GatewayAddress { - // address can either be a hostname (ex: gateway.example.com) or an IP (ex: 1.2.3.4). - oneof destination { - // TODO: add support for hostname lookup - NamespacedHostname hostname = 1; - NetworkAddress address = 2; - } - // port to reach the gateway at for mTLS HBONE connections - uint32 hbone_mtls_port = 3; - reserved "hbone_single_tls_port"; - reserved 4; -} - -// NetworkAddress represents an address bound to a specific network. -message NetworkAddress { - // Network represents the network this address is on. - string network = 1; - // Address presents the IP (v4 or v6). - bytes address = 2; -} - -// NamespacedHostname represents a service bound to a specific namespace. -message NamespacedHostname { - // The namespace the service is in. - string namespace = 1; - // hostname (ex: gateway.example.com) - string hostname = 2; -} diff --git a/source/extensions/common/workload_discovery/extension.proto b/source/extensions/common/workload_discovery/extension.proto deleted file mode 100644 index 412f77f851e..00000000000 --- a/source/extensions/common/workload_discovery/extension.proto +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -import "envoy/config/core/v3/config_source.proto"; - -package istio.workload; -option go_package = "test/envoye2e/workloadapi"; - -message BootstrapExtension { - envoy.config.core.v3.ConfigSource config_source = 1; -} diff --git a/source/extensions/filters/http/alpn/BUILD b/source/extensions/filters/http/alpn/BUILD deleted file mode 100644 index e20c69b83af..00000000000 --- a/source/extensions/filters/http/alpn/BUILD +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright 2019 Istio Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ -# - -load( - "@envoy//bazel:envoy_build_system.bzl", - "envoy_cc_library", - "envoy_cc_test", -) - -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -envoy_cc_library( - name = "alpn_filter", - srcs = ["alpn_filter.cc"], - hdrs = ["alpn_filter.h"], - repository = "@envoy", - deps = [ - ":config_cc_proto", - "@envoy//envoy/http:filter_interface", - "@envoy//source/common/network:application_protocol_lib", - "@envoy//source/extensions/filters/http/common:pass_through_filter_lib", - ], -) - -envoy_cc_library( - name = "config_lib", - srcs = ["config.cc"], - hdrs = ["config.h"], - repository = "@envoy", - deps = [ - ":alpn_filter", - "@envoy//envoy/registry", - "@envoy//source/extensions/filters/http/common:factory_base_lib", - ], -) - -envoy_cc_test( - name = "alpn_test", - srcs = [ - "alpn_test.cc", - ], - repository = "@envoy", - deps = [ - ":alpn_filter", - ":config_lib", - "@envoy//test/mocks/http:http_mocks", - "@envoy//test/mocks/local_info:local_info_mocks", - "@envoy//test/mocks/network:network_mocks", - "@envoy//test/mocks/protobuf:protobuf_mocks", - "@envoy//test/mocks/upstream:upstream_mocks", - ], -) - -envoy_cc_test( - name = "config_test", - srcs = [ - "config_test.cc", - ], - repository = "@envoy", - deps = [ - ":alpn_filter", - ":config_lib", - "@envoy//test/mocks/server:server_mocks", - "@envoy//test/test_common:utility_lib", - ], -) - -cc_proto_library( - name = "config_cc_proto", - deps = ["config"], -) - -proto_library( - name = "config", - srcs = ["config.proto"], -) diff --git a/source/extensions/filters/http/alpn/alpn_filter.cc b/source/extensions/filters/http/alpn/alpn_filter.cc deleted file mode 100644 index 26e14a2c457..00000000000 --- a/source/extensions/filters/http/alpn/alpn_filter.cc +++ /dev/null @@ -1,105 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "source/extensions/filters/http/alpn/alpn_filter.h" - -#include "envoy/upstream/cluster_manager.h" -#include "source/common/network/application_protocol.h" - -namespace Envoy { -namespace Http { -namespace Alpn { - -AlpnFilterConfig::AlpnFilterConfig( - const istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig& proto_config, - Upstream::ClusterManager& cluster_manager) - : cluster_manager_(cluster_manager) { - for (const auto& pair : proto_config.alpn_override()) { - std::vector application_protocols; - for (const auto& protocol : pair.alpn_override()) { - application_protocols.push_back(protocol); - } - - alpn_overrides_.insert( - {getHttpProtocol(pair.upstream_protocol()), std::move(application_protocols)}); - } -} - -Http::Protocol AlpnFilterConfig::getHttpProtocol( - const istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig::Protocol& protocol) { - switch (protocol) { - case istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig::Protocol:: - FilterConfig_Protocol_HTTP10: - return Http::Protocol::Http10; - case istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig::Protocol:: - FilterConfig_Protocol_HTTP11: - return Http::Protocol::Http11; - case istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig::Protocol:: - FilterConfig_Protocol_HTTP2: - return Http::Protocol::Http2; - default: - PANIC("not implemented"); - } -} - -Http::FilterHeadersStatus AlpnFilter::decodeHeaders(Http::RequestHeaderMap&, bool) { - Router::RouteConstSharedPtr route = decoder_callbacks_->route(); - const Router::RouteEntry* route_entry; - if (!route || !(route_entry = route->routeEntry())) { - ENVOY_LOG(debug, "cannot find route entry"); - return Http::FilterHeadersStatus::Continue; - } - - Upstream::ThreadLocalCluster* cluster = - config_->clusterManager().getThreadLocalCluster(route_entry->clusterName()); - if (!cluster || !cluster->info()) { - ENVOY_LOG(debug, "cannot find cluster {}", route_entry->clusterName()); - return Http::FilterHeadersStatus::Continue; - } - - const auto& filter_metadata = cluster->info()->metadata().filter_metadata(); - const auto& istio = filter_metadata.find("istio"); - if (istio != filter_metadata.end()) { - const auto& alpn_override = istio->second.fields().find("alpn_override"); - if (alpn_override != istio->second.fields().end()) { - const auto alpn_override_value = alpn_override->second.string_value(); - if (alpn_override_value == "false") { - // Skip ALPN header rewrite - ENVOY_LOG(debug, - "Skipping ALPN header rewrite because istio.alpn_override metadata is false"); - return Http::FilterHeadersStatus::Continue; - } - } - } - - auto protocols = - cluster->info()->upstreamHttpProtocol(decoder_callbacks_->streamInfo().protocol()); - const auto& alpn_override = config_->alpnOverrides(protocols[0]); - - if (!alpn_override.empty()) { - ENVOY_LOG(debug, "override with {} ALPNs", alpn_override.size()); - decoder_callbacks_->streamInfo().filterState()->setData( - Network::ApplicationProtocols::key(), - std::make_unique(alpn_override), - Envoy::StreamInfo::FilterState::StateType::ReadOnly); - } else { - ENVOY_LOG(debug, "ALPN override is empty"); - } - return Http::FilterHeadersStatus::Continue; -} - -} // namespace Alpn -} // namespace Http -} // namespace Envoy diff --git a/source/extensions/filters/http/alpn/alpn_filter.h b/source/extensions/filters/http/alpn/alpn_filter.h deleted file mode 100644 index 9b1144240b2..00000000000 --- a/source/extensions/filters/http/alpn/alpn_filter.h +++ /dev/null @@ -1,66 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "source/extensions/filters/http/alpn/config.pb.h" -#include "source/extensions/filters/http/common/pass_through_filter.h" - -namespace Envoy { -namespace Http { -namespace Alpn { - -using AlpnOverrides = absl::flat_hash_map>; - -class AlpnFilterConfig { -public: - AlpnFilterConfig( - const istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig& proto_config, - Upstream::ClusterManager& cluster_manager); - - Upstream::ClusterManager& clusterManager() { return cluster_manager_; } - - const std::vector alpnOverrides(const Http::Protocol& protocol) const { - if (alpn_overrides_.count(protocol)) { - return alpn_overrides_.at(protocol); - } - return {}; - } - -private: - Http::Protocol getHttpProtocol( - const istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig::Protocol& protocol); - - AlpnOverrides alpn_overrides_; - Upstream::ClusterManager& cluster_manager_; -}; - -using AlpnFilterConfigSharedPtr = std::shared_ptr; - -class AlpnFilter : public Http::PassThroughDecoderFilter, Logger::Loggable { -public: - explicit AlpnFilter(const AlpnFilterConfigSharedPtr& config) : config_(config) {} - - // Http::PassThroughDecoderFilter - Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap& headers, - bool end_stream) override; - -private: - const AlpnFilterConfigSharedPtr config_; -}; - -} // namespace Alpn -} // namespace Http -} // namespace Envoy diff --git a/source/extensions/filters/http/alpn/alpn_test.cc b/source/extensions/filters/http/alpn/alpn_test.cc deleted file mode 100644 index 8cc5d2767a5..00000000000 --- a/source/extensions/filters/http/alpn/alpn_test.cc +++ /dev/null @@ -1,194 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "source/common/network/application_protocol.h" -#include "source/extensions/filters/http/alpn/alpn_filter.h" -#include "test/mocks/http/mocks.h" -#include "test/mocks/upstream/mocks.h" - -using istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig; -using istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig_AlpnOverride; -using testing::NiceMock; -using testing::Return; -using testing::ReturnRef; - -namespace Envoy { -namespace Http { -namespace Alpn { -namespace { - -class AlpnFilterTest : public testing::Test { -public: - std::unique_ptr makeAlpnOverrideFilter(const AlpnOverrides& alpn) { - FilterConfig proto_config; - - for (const auto& p : alpn) { - FilterConfig_AlpnOverride entry; - entry.set_upstream_protocol(getProtocol(p.first)); - for (const auto& v : p.second) { - entry.add_alpn_override(v); - } - proto_config.mutable_alpn_override()->Add(std::move(entry)); - } - - auto config = std::make_shared(proto_config, cluster_manager_); - auto filter = std::make_unique(config); - filter->setDecoderFilterCallbacks(callbacks_); - return filter; - } - -protected: - FilterConfig::Protocol getProtocol(Http::Protocol protocol) { - switch (protocol) { - case Http::Protocol::Http10: - return FilterConfig::Protocol::FilterConfig_Protocol_HTTP10; - case Http::Protocol::Http11: - return FilterConfig::Protocol::FilterConfig_Protocol_HTTP11; - case Http::Protocol::Http2: - return FilterConfig::Protocol::FilterConfig_Protocol_HTTP2; - default: - PANIC("not implemented"); - } - } - - NiceMock callbacks_; - NiceMock cluster_manager_; - std::shared_ptr fake_cluster_{ - std::make_shared>()}; - std::shared_ptr cluster_info_{ - std::make_shared>()}; - Http::TestRequestHeaderMapImpl headers_; -}; - -TEST_F(AlpnFilterTest, OverrideAlpnUseDownstreamProtocol) { - NiceMock stream_info; - ON_CALL(callbacks_, streamInfo()).WillByDefault(ReturnRef(stream_info)); - const AlpnOverrides alpn = {{Http::Protocol::Http10, {"foo", "bar"}}, - {Http::Protocol::Http11, {"baz"}}, - {Http::Protocol::Http2, {"qux"}}}; - auto filter = makeAlpnOverrideFilter(alpn); - - ON_CALL(cluster_manager_, getThreadLocalCluster(_)).WillByDefault(Return(fake_cluster_.get())); - ON_CALL(*fake_cluster_, info()).WillByDefault(Return(cluster_info_)); - ON_CALL(*cluster_info_, upstreamHttpProtocol(_)) - .WillByDefault([](absl::optional protocol) -> std::vector { - return {protocol.value()}; - }); - - auto protocols = {Http::Protocol::Http10, Http::Protocol::Http11, Http::Protocol::Http2}; - for (const auto p : protocols) { - EXPECT_CALL(stream_info, protocol()).WillOnce(Return(p)); - Envoy::StreamInfo::FilterStateSharedPtr filter_state( - std::make_shared( - Envoy::StreamInfo::FilterState::LifeSpan::FilterChain)); - EXPECT_CALL(stream_info, filterState()).WillOnce(ReturnRef(filter_state)); - EXPECT_EQ(filter->decodeHeaders(headers_, false), Http::FilterHeadersStatus::Continue); - EXPECT_TRUE( - filter_state->hasData(Network::ApplicationProtocols::key())); - auto alpn_override = - filter_state - ->getDataReadOnly(Network::ApplicationProtocols::key()) - ->value(); - - EXPECT_EQ(alpn_override, alpn.at(p)); - } -} - -TEST_F(AlpnFilterTest, OverrideAlpn) { - NiceMock stream_info; - ON_CALL(callbacks_, streamInfo()).WillByDefault(ReturnRef(stream_info)); - const AlpnOverrides alpn = {{Http::Protocol::Http10, {"foo", "bar"}}, - {Http::Protocol::Http11, {"baz"}}, - {Http::Protocol::Http2, {"qux"}}}; - auto filter = makeAlpnOverrideFilter(alpn); - - ON_CALL(cluster_manager_, getThreadLocalCluster(_)).WillByDefault(Return(fake_cluster_.get())); - ON_CALL(*fake_cluster_, info()).WillByDefault(Return(cluster_info_)); - ON_CALL(*cluster_info_, upstreamHttpProtocol(_)) - .WillByDefault([](absl::optional) -> std::vector { - return {Http::Protocol::Http2}; - }); - - auto protocols = {Http::Protocol::Http10, Http::Protocol::Http11, Http::Protocol::Http2}; - for (const auto p : protocols) { - EXPECT_CALL(stream_info, protocol()).WillOnce(Return(p)); - Envoy::StreamInfo::FilterStateSharedPtr filter_state( - std::make_shared( - Envoy::StreamInfo::FilterState::LifeSpan::FilterChain)); - EXPECT_CALL(stream_info, filterState()).WillOnce(ReturnRef(filter_state)); - EXPECT_EQ(filter->decodeHeaders(headers_, false), Http::FilterHeadersStatus::Continue); - EXPECT_TRUE( - filter_state->hasData(Network::ApplicationProtocols::key())); - auto alpn_override = - filter_state - ->getDataReadOnly(Network::ApplicationProtocols::key()) - ->value(); - - EXPECT_EQ(alpn_override, alpn.at(Http::Protocol::Http2)); - } -} - -TEST_F(AlpnFilterTest, EmptyOverrideAlpn) { - NiceMock stream_info; - ON_CALL(callbacks_, streamInfo()).WillByDefault(ReturnRef(stream_info)); - const AlpnOverrides alpn = {{Http::Protocol::Http10, {"foo", "bar"}}, - {Http::Protocol::Http11, {"baz"}}}; - auto filter = makeAlpnOverrideFilter(alpn); - - ON_CALL(cluster_manager_, getThreadLocalCluster(_)).WillByDefault(Return(fake_cluster_.get())); - ON_CALL(*fake_cluster_, info()).WillByDefault(Return(cluster_info_)); - ON_CALL(*cluster_info_, upstreamHttpProtocol(_)) - .WillByDefault([](absl::optional) -> std::vector { - return {Http::Protocol::Http2}; - }); - - auto protocols = {Http::Protocol::Http10, Http::Protocol::Http11, Http::Protocol::Http2}; - for (const auto p : protocols) { - EXPECT_CALL(stream_info, protocol()).WillOnce(Return(p)); - Envoy::StreamInfo::FilterStateImpl filter_state{Envoy::StreamInfo::FilterState::FilterChain}; - EXPECT_CALL(stream_info, filterState()).Times(0); - EXPECT_EQ(filter->decodeHeaders(headers_, false), Http::FilterHeadersStatus::Continue); - EXPECT_FALSE( - filter_state.hasData(Network::ApplicationProtocols::key())); - } -} - -TEST_F(AlpnFilterTest, AlpnOverrideFalse) { - NiceMock stream_info; - auto metadata = TestUtility::parseYaml(R"EOF( - filter_metadata: - istio: - alpn_override: "false" - )EOF"); - - ON_CALL(callbacks_, streamInfo()).WillByDefault(ReturnRef(stream_info)); - ON_CALL(cluster_manager_, getThreadLocalCluster(_)).WillByDefault(Return(fake_cluster_.get())); - ON_CALL(*fake_cluster_, info()).WillByDefault(Return(cluster_info_)); - ON_CALL(*cluster_info_, metadata()).WillByDefault(ReturnRef(metadata)); - - const AlpnOverrides alpn = {{Http::Protocol::Http10, {"foo", "bar"}}, - {Http::Protocol::Http11, {"baz"}}}; - auto filter = makeAlpnOverrideFilter(alpn); - - EXPECT_CALL(*cluster_info_, upstreamHttpProtocol(_)).Times(0); - EXPECT_EQ(filter->decodeHeaders(headers_, false), Http::FilterHeadersStatus::Continue); -} - -} // namespace -} // namespace Alpn -} // namespace Http -} // namespace Envoy diff --git a/source/extensions/filters/http/alpn/config.cc b/source/extensions/filters/http/alpn/config.cc deleted file mode 100644 index 51f2a6182b1..00000000000 --- a/source/extensions/filters/http/alpn/config.cc +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "source/extensions/filters/http/alpn/config.h" - -#include "source/common/protobuf/message_validator_impl.h" -#include "source/extensions/filters/http/alpn/alpn_filter.h" - -using istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig; - -namespace Envoy { -namespace Http { -namespace Alpn { -absl::StatusOr -AlpnConfigFactory::createFilterFactoryFromProto(const Protobuf::Message& config, const std::string&, - Server::Configuration::FactoryContext& context) { - return createFilterFactory(dynamic_cast(config), - context.serverFactoryContext().clusterManager()); -} - -ProtobufTypes::MessagePtr AlpnConfigFactory::createEmptyConfigProto() { - return ProtobufTypes::MessagePtr{new FilterConfig}; -} - -std::string AlpnConfigFactory::name() const { return "istio.alpn"; } - -Http::FilterFactoryCb -AlpnConfigFactory::createFilterFactory(const FilterConfig& proto_config, - Upstream::ClusterManager& cluster_manager) { - AlpnFilterConfigSharedPtr filter_config{ - std::make_shared(proto_config, cluster_manager)}; - return [filter_config](Http::FilterChainFactoryCallbacks& callbacks) -> void { - callbacks.addStreamDecoderFilter(std::make_unique(filter_config)); - }; -} - -/** - * Static registration for the alpn override filter. @see RegisterFactory. - */ -REGISTER_FACTORY(AlpnConfigFactory, Server::Configuration::NamedHttpFilterConfigFactory); - -} // namespace Alpn -} // namespace Http -} // namespace Envoy diff --git a/source/extensions/filters/http/alpn/config.h b/source/extensions/filters/http/alpn/config.h deleted file mode 100644 index a4d991ada77..00000000000 --- a/source/extensions/filters/http/alpn/config.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "source/extensions/filters/http/alpn/config.pb.h" -#include "source/extensions/filters/http/common/factory_base.h" - -namespace Envoy { -namespace Http { -namespace Alpn { - -/** - * Config registration for the alpn filter. - */ -class AlpnConfigFactory : public Server::Configuration::NamedHttpFilterConfigFactory { -public: - // Server::Configuration::NamedHttpFilterConfigFactory - absl::StatusOr - createFilterFactoryFromProto(const Protobuf::Message& config, const std::string& stat_prefix, - Server::Configuration::FactoryContext& context) override; - ProtobufTypes::MessagePtr createEmptyConfigProto() override; - std::string name() const override; - -private: - Http::FilterFactoryCb createFilterFactory( - const istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig& config_pb, - Upstream::ClusterManager& cluster_manager); -}; - -} // namespace Alpn -} // namespace Http -} // namespace Envoy diff --git a/source/extensions/filters/http/alpn/config.proto b/source/extensions/filters/http/alpn/config.proto deleted file mode 100644 index 64d3e17cc6e..00000000000 --- a/source/extensions/filters/http/alpn/config.proto +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2018 Istio Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -// $title: ALPN filter for overriding ALPN for upstream TLS connections. - -package istio.envoy.config.filter.http.alpn.v2alpha1; - -option go_package = "istio.io/api/envoy/config/filter/http/alpn/v2alpha1"; - -// FilterConfig is the config for Istio-specific filter. -message FilterConfig { - // Upstream protocols - enum Protocol { - HTTP10 = 0; - HTTP11 = 1; - HTTP2 = 2; - } - - message AlpnOverride { - // Upstream protocol - Protocol upstream_protocol = 1; - // A list of ALPN that will override the ALPN for upstream TLS connections. - repeated string alpn_override = 2; - } - - // Map from upstream protocol to list of ALPN - repeated AlpnOverride alpn_override = 1; -} diff --git a/source/extensions/filters/http/alpn/config_test.cc b/source/extensions/filters/http/alpn/config_test.cc deleted file mode 100644 index e66790d175c..00000000000 --- a/source/extensions/filters/http/alpn/config_test.cc +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "source/extensions/filters/http/alpn/config.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "source/extensions/filters/http/alpn/alpn_filter.h" -#include "test/mocks/network/mocks.h" -#include "test/mocks/server/mocks.h" -#include "test/mocks/stream_info/mocks.h" -#include "test/test_common/utility.h" - -using testing::_; -using testing::NiceMock; -using testing::Return; -using testing::ReturnRef; - -using istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig; - -namespace Envoy { -namespace Http { -namespace Alpn { -namespace { - -TEST(AlpnFilterConfigTest, OverrideAlpn) { - const std::string yaml = R"EOF( - alpn_override: - - upstream_protocol: HTTP10 - alpn_override: ["foo", "bar"] - - upstream_protocol: HTTP11 - alpn_override: ["baz"] - - upstream_protocol: HTTP2 - alpn_override: ["qux"] - )EOF"; - - FilterConfig proto_config; - TestUtility::loadFromYaml(yaml, proto_config); - AlpnConfigFactory factory; - NiceMock context; - Http::FilterFactoryCb cb = - factory.createFilterFactoryFromProto(proto_config, "stats", context).value(); - Http::MockFilterChainFactoryCallbacks filter_callback; - Http::StreamDecoderFilterSharedPtr added_filter; - EXPECT_CALL(filter_callback, addStreamDecoderFilter(_)) - .WillOnce(Invoke([&added_filter](Http::StreamDecoderFilterSharedPtr filter) { - added_filter = std::move(filter); - })); - - cb(filter_callback); - EXPECT_NE(dynamic_cast(added_filter.get()), nullptr); -} - -} // namespace -} // namespace Alpn -} // namespace Http -} // namespace Envoy diff --git a/source/extensions/filters/http/istio_stats/BUILD b/source/extensions/filters/http/istio_stats/BUILD deleted file mode 100644 index 26678ae8781..00000000000 --- a/source/extensions/filters/http/istio_stats/BUILD +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright 2018 Istio Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ -# - -load( - "@envoy//bazel:envoy_build_system.bzl", - "envoy_cc_library", -) - -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -envoy_cc_library( - name = "istio_stats", - srcs = ["istio_stats.cc"], - hdrs = ["istio_stats.h"], - repository = "@envoy", - deps = [ - ":config_cc_proto", - "//extensions/common:metadata_object_lib", - "@com_google_cel_cpp//eval/public:builtin_func_registrar", - "@com_google_cel_cpp//eval/public:cel_expr_builder_factory", - "@com_google_cel_cpp//parser", - "@envoy//envoy/registry", - "@envoy//envoy/router:string_accessor_interface", - "@envoy//envoy/server:factory_context_interface", - "@envoy//envoy/server:filter_config_interface", - "@envoy//envoy/singleton:manager_interface", - "@envoy//envoy/stream_info:filter_state_interface", - "@envoy//source/common/grpc:common_lib", - "@envoy//source/common/http:header_map_lib", - "@envoy//source/common/http:header_utility_lib", - "@envoy//source/common/network:utility_lib", - "@envoy//source/common/stream_info:utility_lib", - "@envoy//source/extensions/filters/common/expr:context_lib", - "@envoy//source/extensions/filters/common/expr:evaluator_lib", - "@envoy//source/extensions/filters/http/common:pass_through_filter_lib", - "@envoy//source/extensions/filters/http/grpc_stats:config", - ], -) - -cc_proto_library( - name = "config_cc_proto", - deps = ["config"], -) - -proto_library( - name = "config", - srcs = ["config.proto"], - deps = [ - "@com_google_protobuf//:duration_proto", - ], -) diff --git a/source/extensions/filters/http/istio_stats/config.proto b/source/extensions/filters/http/istio_stats/config.proto deleted file mode 100644 index 5f50e33e698..00000000000 --- a/source/extensions/filters/http/istio_stats/config.proto +++ /dev/null @@ -1,124 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto3"; - -// $title: Stats Config -// $description: Configuration for Stats Filter. -// $location: https://istio.io/docs/reference/config/proxy_extensions/stats.html -// $weight: 20 - -package stats; - -import "google/protobuf/duration.proto"; - -// Metric instance configuration overrides. -// The metric value and the metric type are optional and permit changing the -// reported value for an existing metric. -// The standard metrics are optimized and reported through a "fast-path". -// The customizations allow full configurability, at the cost of a "slower" -// path. -message MetricConfig { - // (Optional) Collection of tag names and tag expressions to include in the - // metric. Conflicts are resolved by the tag name by overriding previously - // supplied values. - map dimensions = 1; - - // (Optional) Metric name to restrict the override to a metric. If not - // specified, applies to all. - string name = 2; - - // (Optional) A list of tags to remove. - repeated string tags_to_remove = 3; - - // NOT IMPLEMENTED. (Optional) Conditional enabling the override. - string match = 4; - - // (Optional) If this is set to true, the metric(s) selected by this - // configuration will not be generated or reported. - bool drop = 5; -} - -enum MetricType { - COUNTER = 0; - GAUGE = 1; - HISTOGRAM = 2; -} - -message MetricDefinition { - // Metric name. - string name = 1; - - // Metric value expression. - string value = 2; - - // Metric type. - MetricType type = 3; -} - -// Specifies the proxy deployment type. -enum Reporter { - // Default value is inferred from the listener direction, as either client or - // server sidecar. - UNSPECIFIED = 0; - - // Shared server gateway, e.g. "waypoint". - SERVER_GATEWAY = 1; -} - -message PluginConfig { - reserved 1; - reserved "debug"; - - reserved 2; - reserved "max_peer_cache_size"; - - reserved 3; - reserved "stat_prefix"; - - reserved 4; - reserved "field_separator"; - - reserved 5; - reserved "value_separator"; - - // Optional: Disable using host header as a fallback if destination service is - // not available from the controlplane. Disable the fallback if the host - // header originates outsides the mesh, like at ingress. - bool disable_host_header_fallback = 6; - - // Optional. Allows configuration of the time between calls out to for TCP - // metrics reporting. The default duration is `5s`. - google.protobuf.Duration tcp_reporting_duration = 7; - - // Metric overrides. - repeated MetricConfig metrics = 8; - - // Metric definitions. - repeated MetricDefinition definitions = 9; - - // Proxy deployment type. - Reporter reporter = 10; - - // Metric scope rotation interval. Set to 0 to disable the metric scope rotation. - // Defaults to 0. - // DEPRECATED. - google.protobuf.Duration rotation_interval = 11; - - // Metric expiry graceful deletion interval. No-op if the metric rotation is disabled. - // Defaults to 5m. Must be >=1s. - // DEPRECATED. - google.protobuf.Duration graceful_deletion_interval = 12; -} diff --git a/source/extensions/filters/http/istio_stats/istio_stats.cc b/source/extensions/filters/http/istio_stats/istio_stats.cc deleted file mode 100644 index 9bedf042928..00000000000 --- a/source/extensions/filters/http/istio_stats/istio_stats.cc +++ /dev/null @@ -1,1285 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "source/extensions/filters/http/istio_stats/istio_stats.h" - -#include - -#include "envoy/router/string_accessor.h" -#include "envoy/registry/registry.h" -#include "envoy/server/factory_context.h" -#include "envoy/singleton/manager.h" -#include "extensions/common/metadata_object.h" -#include "parser/parser.h" -#include "source/common/grpc/common.h" -#include "source/common/http/header_map_impl.h" -#include "source/common/http/header_utility.h" -#include "source/common/network/utility.h" -#include "source/common/stream_info/utility.h" -#include "source/extensions/filters/common/expr/context.h" -#include "source/extensions/filters/common/expr/cel_state.h" -#include "source/extensions/filters/common/expr/evaluator.h" -#include "source/extensions/filters/http/common/pass_through_filter.h" -#include "source/extensions/filters/http/grpc_stats/grpc_stats_filter.h" - -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#endif - -#include "eval/public/builtin_func_registrar.h" -#include "eval/public/cel_expr_builder_factory.h" - -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - -namespace Envoy { -namespace Extensions { -namespace HttpFilters { -namespace IstioStats { - -namespace { - -constexpr absl::string_view NamespaceKey = "/ns/"; - -absl::optional getNamespace(absl::string_view principal) { - // The namespace is a substring in principal with format: - // "/ns//sa/". '/' is not allowed to - // appear in actual content except as delimiter between tokens. - size_t begin = principal.find(NamespaceKey); - if (begin == absl::string_view::npos) { - return {}; - } - begin += NamespaceKey.length(); - size_t end = principal.find('/', begin); - size_t len = (end == std::string::npos ? end : end - begin); - return {principal.substr(begin, len)}; -} - -constexpr absl::string_view CustomStatNamespace = "istiocustom"; - -absl::string_view extractString(const Protobuf::Struct& metadata, absl::string_view key) { - const auto& it = metadata.fields().find(key); - if (it == metadata.fields().end()) { - return {}; - } - return it->second.string_value(); -} - -absl::string_view extractMapString(const Protobuf::Struct& metadata, const std::string& map_key, - absl::string_view key) { - const auto& it = metadata.fields().find(map_key); - if (it == metadata.fields().end()) { - return {}; - } - return extractString(it->second.struct_value(), key); -} - -absl::optional -extractEndpointMetadata(const StreamInfo::StreamInfo& info) { - auto upstream_info = info.upstreamInfo(); - auto upstream_host = upstream_info ? upstream_info->upstreamHost() : nullptr; - if (upstream_host && upstream_host->metadata()) { - const auto& filter_metadata = upstream_host->metadata()->filter_metadata(); - const auto& it = filter_metadata.find("istio"); - if (it != filter_metadata.end()) { - const auto& workload_it = it->second.fields().find("workload"); - if (workload_it != it->second.fields().end()) { - return Istio::Common::convertEndpointMetadata(workload_it->second.string_value()); - } - } - } - return {}; -} - -enum class Reporter { - // Regular outbound listener on a sidecar. - ClientSidecar, - // Regular inbound listener on a sidecar. - ServerSidecar, - // Gateway listener for a set of destination workloads. - ServerGateway, -}; - -// Detect if peer info read is completed by TCP metadata exchange. -bool peerInfoRead(Reporter reporter, const StreamInfo::FilterState& filter_state) { - const auto& filter_state_key = - reporter == Reporter::ServerSidecar || reporter == Reporter::ServerGateway - ? Istio::Common::DownstreamPeer - : Istio::Common::UpstreamPeer; - return filter_state.hasDataWithName(filter_state_key) || - filter_state.hasDataWithName(Istio::Common::NoPeer); -} - -std::optional -peerInfo(Reporter reporter, const StreamInfo::FilterState& filter_state) { - const auto& filter_state_key = - reporter == Reporter::ServerSidecar || reporter == Reporter::ServerGateway - ? Istio::Common::DownstreamPeer - : Istio::Common::UpstreamPeer; - // This's a workaround before FilterStateObject support operation like `.labels['role']`. - // The workaround is to use CelState to store the peer metadata. - // Rebuild the WorkloadMetadataObject from the CelState. - const auto* cel_state = - filter_state.getDataReadOnly( - filter_state_key); - if (!cel_state) { - return {}; - } - - Protobuf::Struct obj; - if (!obj.ParseFromString(absl::string_view(cel_state->value()))) { - return {}; - } - - Istio::Common::WorkloadMetadataObject peer_info( - extractString(obj, Istio::Common::InstanceNameToken), - extractString(obj, Istio::Common::ClusterNameToken), - extractString(obj, Istio::Common::NamespaceNameToken), - extractString(obj, Istio::Common::WorkloadNameToken), - extractString(obj, Istio::Common::ServiceNameToken), - extractString(obj, Istio::Common::ServiceVersionToken), - extractString(obj, Istio::Common::AppNameToken), - extractString(obj, Istio::Common::AppVersionToken), - Istio::Common::fromSuffix(extractString(obj, Istio::Common::WorkloadTypeToken)), - extractString(obj, Istio::Common::IdentityToken)); - - return peer_info; -} - -// Process-wide context shared with all filter instances. -struct Context : public Singleton::Instance { - explicit Context(Stats::Scope& scope, const LocalInfo::LocalInfo& local_info) - : pool_(scope.symbolTable()), local_info_(local_info), - stat_namespace_(pool_.add(CustomStatNamespace)), - requests_total_(pool_.add("istio_requests_total")), - request_duration_milliseconds_(pool_.add("istio_request_duration_milliseconds")), - request_bytes_(pool_.add("istio_request_bytes")), - response_bytes_(pool_.add("istio_response_bytes")), - request_messages_total_(pool_.add("istio_request_messages_total")), - response_messages_total_(pool_.add("istio_response_messages_total")), - tcp_connections_opened_total_(pool_.add("istio_tcp_connections_opened_total")), - tcp_connections_closed_total_(pool_.add("istio_tcp_connections_closed_total")), - tcp_sent_bytes_total_(pool_.add("istio_tcp_sent_bytes_total")), - tcp_received_bytes_total_(pool_.add("istio_tcp_received_bytes_total")), - empty_(pool_.add("")), unknown_(pool_.add("unknown")), source_(pool_.add("source")), - destination_(pool_.add("destination")), latest_(pool_.add("latest")), - http_(pool_.add("http")), grpc_(pool_.add("grpc")), tcp_(pool_.add("tcp")), - mutual_tls_(pool_.add("mutual_tls")), none_(pool_.add("none")), - reporter_(pool_.add("reporter")), source_workload_(pool_.add("source_workload")), - source_workload_namespace_(pool_.add("source_workload_namespace")), - source_principal_(pool_.add("source_principal")), source_app_(pool_.add("source_app")), - source_version_(pool_.add("source_version")), - source_canonical_service_(pool_.add("source_canonical_service")), - source_canonical_revision_(pool_.add("source_canonical_revision")), - source_cluster_(pool_.add("source_cluster")), - destination_workload_(pool_.add("destination_workload")), - destination_workload_namespace_(pool_.add("destination_workload_namespace")), - destination_principal_(pool_.add("destination_principal")), - destination_app_(pool_.add("destination_app")), - destination_version_(pool_.add("destination_version")), - destination_service_(pool_.add("destination_service")), - destination_service_name_(pool_.add("destination_service_name")), - destination_service_namespace_(pool_.add("destination_service_namespace")), - destination_canonical_service_(pool_.add("destination_canonical_service")), - destination_canonical_revision_(pool_.add("destination_canonical_revision")), - destination_cluster_(pool_.add("destination_cluster")), - request_protocol_(pool_.add("request_protocol")), - response_flags_(pool_.add("response_flags")), - connection_security_policy_(pool_.add("connection_security_policy")), - response_code_(pool_.add("response_code")), - grpc_response_status_(pool_.add("grpc_response_status")), - workload_name_(pool_.add(extractString(local_info.node().metadata(), "WORKLOAD_NAME"))), - namespace_(pool_.add(extractString(local_info.node().metadata(), "NAMESPACE"))), - canonical_name_(pool_.add(extractMapString(local_info.node().metadata(), "LABELS", - Istio::Common::CanonicalNameLabel))), - canonical_revision_(pool_.add(extractMapString(local_info.node().metadata(), "LABELS", - Istio::Common::CanonicalRevisionLabel))), - app_name_(pool_.add( - extractMapString(local_info.node().metadata(), "LABELS", Istio::Common::AppNameLabel))), - app_version_(pool_.add(extractMapString(local_info.node().metadata(), "LABELS", - Istio::Common::AppVersionLabel))), - cluster_name_(pool_.add(extractString(local_info.node().metadata(), "CLUSTER_ID"))), - waypoint_(pool_.add("waypoint")), istio_build_(pool_.add("istio_build")), - component_(pool_.add("component")), proxy_(pool_.add("proxy")), tag_(pool_.add("tag")), - istio_version_(pool_.add(extractString(local_info.node().metadata(), "ISTIO_VERSION"))), - scope_(scope.createScope("", true)) { - all_metrics_ = { - {"requests_total", requests_total_}, - {"request_duration_milliseconds", request_duration_milliseconds_}, - {"request_bytes", request_bytes_}, - {"response_bytes", response_bytes_}, - {"request_messages_total", request_messages_total_}, - {"response_messages_total", response_messages_total_}, - {"tcp_connections_opened_total", tcp_connections_opened_total_}, - {"tcp_connections_closed_total", tcp_connections_closed_total_}, - {"tcp_sent_bytes_total", tcp_sent_bytes_total_}, - {"tcp_received_bytes_total", tcp_received_bytes_total_}, - }; - all_tags_ = { - {"reporter", reporter_}, - {"source_workload", source_workload_}, - {"source_workload_namespace", source_workload_namespace_}, - {"source_principal", source_principal_}, - {"source_app", source_app_}, - {"source_version", source_version_}, - {"source_canonical_service", source_canonical_service_}, - {"source_canonical_revision", source_canonical_revision_}, - {"source_cluster", source_cluster_}, - {"destination_workload", destination_workload_}, - {"destination_workload_namespace", destination_workload_namespace_}, - {"destination_principal", destination_principal_}, - {"destination_app", destination_app_}, - {"destination_version", destination_version_}, - {"destination_service", destination_service_}, - {"destination_service_name", destination_service_name_}, - {"destination_service_namespace", destination_service_namespace_}, - {"destination_canonical_service", destination_canonical_service_}, - {"destination_canonical_revision", destination_canonical_revision_}, - {"destination_cluster", destination_cluster_}, - {"request_protocol", request_protocol_}, - {"response_flags", response_flags_}, - {"connection_security_policy", connection_security_policy_}, - {"response_code", response_code_}, - {"grpc_response_status", grpc_response_status_}, - }; - } - - Stats::StatNamePool pool_; - const LocalInfo::LocalInfo& local_info_; - absl::flat_hash_map all_metrics_; - absl::flat_hash_map all_tags_; - - // Metric names. - const Stats::StatName stat_namespace_; - const Stats::StatName requests_total_; - const Stats::StatName request_duration_milliseconds_; - const Stats::StatName request_bytes_; - const Stats::StatName response_bytes_; - const Stats::StatName request_messages_total_; - const Stats::StatName response_messages_total_; - const Stats::StatName tcp_connections_opened_total_; - const Stats::StatName tcp_connections_closed_total_; - const Stats::StatName tcp_sent_bytes_total_; - const Stats::StatName tcp_received_bytes_total_; - - // Constant names. - const Stats::StatName empty_; - const Stats::StatName unknown_; - const Stats::StatName source_; - const Stats::StatName destination_; - const Stats::StatName latest_; - const Stats::StatName http_; - const Stats::StatName grpc_; - const Stats::StatName tcp_; - const Stats::StatName mutual_tls_; - const Stats::StatName none_; - - // Tag names. - const Stats::StatName reporter_; - - const Stats::StatName source_workload_; - const Stats::StatName source_workload_namespace_; - const Stats::StatName source_principal_; - const Stats::StatName source_app_; - const Stats::StatName source_version_; - const Stats::StatName source_canonical_service_; - const Stats::StatName source_canonical_revision_; - const Stats::StatName source_cluster_; - - const Stats::StatName destination_workload_; - const Stats::StatName destination_workload_namespace_; - const Stats::StatName destination_principal_; - const Stats::StatName destination_app_; - const Stats::StatName destination_version_; - const Stats::StatName destination_service_; - const Stats::StatName destination_service_name_; - const Stats::StatName destination_service_namespace_; - const Stats::StatName destination_canonical_service_; - const Stats::StatName destination_canonical_revision_; - const Stats::StatName destination_cluster_; - - const Stats::StatName request_protocol_; - const Stats::StatName response_flags_; - const Stats::StatName connection_security_policy_; - const Stats::StatName response_code_; - const Stats::StatName grpc_response_status_; - - // Per-process constants. - const Stats::StatName workload_name_; - const Stats::StatName namespace_; - const Stats::StatName canonical_name_; - const Stats::StatName canonical_revision_; - const Stats::StatName app_name_; - const Stats::StatName app_version_; - const Stats::StatName cluster_name_; - const Stats::StatName waypoint_; - - // istio_build metric: - // Publishes Istio version for the proxy as a gauge, sample data: - // testdata/metric/istio_build.yaml - // Sample value for istio_version: "1.17.0" - const Stats::StatName istio_build_; - const Stats::StatName component_; - const Stats::StatName proxy_; - const Stats::StatName tag_; - const Stats::StatName istio_version_; - - // Shared evictable stats scope - Stats::ScopeSharedPtr scope_; -}; // namespace - -using ContextSharedPtr = std::shared_ptr; - -SINGLETON_MANAGER_REGISTRATION(Context) - -// Instructions on dropping, creating, and overriding labels. -// This is not the "hot path" of the metrics system and thus, fairly -// unoptimized. -struct MetricOverrides : public Logger::Loggable { - MetricOverrides(ContextSharedPtr& context, Stats::SymbolTable& symbol_table) - : context_(context), pool_(symbol_table) {} - ContextSharedPtr context_; - Stats::StatNameDynamicPool pool_; - - enum class MetricType { - Counter, - Gauge, - Histogram, - }; - struct CustomMetric { - Stats::StatName name_; - uint32_t expr_; - MetricType type_; - explicit CustomMetric(Stats::StatName name, uint32_t expr, MetricType type) - : name_(name), expr_(expr), type_(type) {} - }; - absl::flat_hash_map custom_metrics_; - // Initial transformation: metrics dropped. - absl::flat_hash_set drop_; - // Second transformation: tags changed. - using TagOverrides = absl::flat_hash_map>; - absl::flat_hash_map tag_overrides_; - // Third transformation: tags added. - using TagAdditions = std::vector>; - absl::flat_hash_map tag_additions_; - - Stats::StatNameTagVector - overrideTags(Stats::StatName metric, const Stats::StatNameTagVector& tags, - const std::vector>& expr_values) { - Stats::StatNameTagVector out; - out.reserve(tags.size()); - const auto& tag_overrides_it = tag_overrides_.find(metric); - if (tag_overrides_it == tag_overrides_.end()) { - out = tags; - } else - for (const auto& [key, val] : tags) { - const auto& it = tag_overrides_it->second.find(key); - if (it != tag_overrides_it->second.end()) { - if (it->second.has_value()) { - out.push_back({key, expr_values[it->second.value()].first}); - } else { - // Skip dropped tags. - } - } else { - out.push_back({key, val}); - } - } - const auto& tag_additions_it = tag_additions_.find(metric); - if (tag_additions_it != tag_additions_.end()) { - for (const auto& [tag, id] : tag_additions_it->second) { - out.push_back({tag, expr_values[id].first}); - } - } - return out; - } - absl::optional getOrCreateExpression(const std::string& expr, bool int_expr) { - const auto& it = expression_ids_.find(expr); - if (it != expression_ids_.end()) { - return {it->second}; - } - auto parse_status = google::api::expr::parser::Parse(expr); - if (!parse_status.ok()) { - return {}; - } - if (expr_builder_ == nullptr) { - google::api::expr::runtime::InterpreterOptions options; - auto builder = google::api::expr::runtime::CreateCelExpressionBuilder(options); - auto register_status = - google::api::expr::runtime::RegisterBuiltinFunctions(builder->GetRegistry(), options); - if (!register_status.ok()) { - throw Extensions::Filters::Common::Expr::CelException( - absl::StrCat("failed to register built-in functions: ", register_status.message())); - } - expr_builder_ = - std::make_shared(std::move(builder)); - } - const auto& parsed_expr = parse_status.value(); - const cel::expr::Expr& cel_expr = parsed_expr.expr(); - - parsed_exprs_.push_back(cel_expr); - auto compiled_expr = Extensions::Filters::Common::Expr::CompiledExpression::Create( - expr_builder_, parsed_exprs_.back()); - if (!compiled_expr.ok()) { - throw Extensions::Filters::Common::Expr::CelException( - absl::StrCat("failed to create compiled expression: ", compiled_expr.status().message())); - } - compiled_exprs_.push_back(std::make_pair(std::move(compiled_expr.value()), int_expr)); - uint32_t id = compiled_exprs_.size() - 1; - expression_ids_.emplace(expr, id); - return {id}; - } - Extensions::Filters::Common::Expr::BuilderInstanceSharedConstPtr expr_builder_; - std::vector parsed_exprs_; - std::vector> compiled_exprs_; - absl::flat_hash_map expression_ids_; -}; - -struct Config : public Logger::Loggable { - Config(const stats::PluginConfig& proto_config, - Server::Configuration::FactoryContext& factory_context) - : context_(factory_context.serverFactoryContext().singletonManager().getTyped( - SINGLETON_MANAGER_REGISTERED_NAME(Context), - [&factory_context] { - return std::make_shared(factory_context.serverFactoryContext().scope(), - factory_context.serverFactoryContext().localInfo()); - })), - disable_host_header_fallback_(proto_config.disable_host_header_fallback()), - report_duration_( - PROTOBUF_GET_MS_OR_DEFAULT(proto_config, tcp_reporting_duration, /* 5s */ 5000)) { - recordVersion(factory_context); - reporter_ = Reporter::ClientSidecar; - switch (proto_config.reporter()) { - case stats::Reporter::UNSPECIFIED: - switch (factory_context.listenerInfo().direction()) { - case envoy::config::core::v3::TrafficDirection::INBOUND: - reporter_ = Reporter::ServerSidecar; - break; - case envoy::config::core::v3::TrafficDirection::OUTBOUND: - reporter_ = Reporter::ClientSidecar; - break; - default: - break; - } - break; - case stats::Reporter::SERVER_GATEWAY: - reporter_ = Reporter::ServerGateway; - break; - default: - break; - } - if (proto_config.metrics_size() > 0 || proto_config.definitions_size() > 0) { - metric_overrides_ = std::make_unique(context_, scope().symbolTable()); - for (const auto& definition : proto_config.definitions()) { - const auto& it = context_->all_metrics_.find(definition.name()); - if (it != context_->all_metrics_.end()) { - ENVOY_LOG(info, "Re-defining standard metric not allowed:: {}", definition.name()); - continue; - } - auto id = metric_overrides_->getOrCreateExpression(definition.value(), true); - if (!id.has_value()) { - ENVOY_LOG(info, "Failed to parse metric value expression: {}", definition.value()); - continue; - } - auto metric_type = MetricOverrides::MetricType::Counter; - switch (definition.type()) { - case stats::MetricType::GAUGE: - metric_type = MetricOverrides::MetricType::Gauge; - break; - case stats::MetricType::HISTOGRAM: - metric_type = MetricOverrides::MetricType::Histogram; - break; - default: - break; - } - metric_overrides_->custom_metrics_.try_emplace( - definition.name(), - metric_overrides_->pool_.add(absl::StrCat("istio_", definition.name())), id.value(), - metric_type); - } - for (const auto& metric : proto_config.metrics()) { - if (metric.drop()) { - const auto& it = context_->all_metrics_.find(metric.name()); - if (it != context_->all_metrics_.end()) { - metric_overrides_->drop_.insert(it->second); - } - continue; - } - for (const auto& tag : metric.tags_to_remove()) { - const auto& tag_it = context_->all_tags_.find(tag); - if (tag_it == context_->all_tags_.end()) { - ENVOY_LOG(info, "Tag is not standard: {}", tag); - continue; - } - if (!metric.name().empty()) { - const auto& it = context_->all_metrics_.find(metric.name()); - if (it != context_->all_metrics_.end()) { - metric_overrides_->tag_overrides_[it->second][tag_it->second] = {}; - } - } else - for (const auto& [_, metric] : context_->all_metrics_) { - metric_overrides_->tag_overrides_[metric][tag_it->second] = {}; - } - } - // Make order of tags deterministic. - std::vector tags; - tags.reserve(metric.dimensions().size()); - for (const auto& [tag, _] : metric.dimensions()) { - tags.push_back(tag); - } - std::sort(tags.begin(), tags.end()); - for (const auto& tag : tags) { - const std::string& expr = metric.dimensions().at(tag); - auto id = metric_overrides_->getOrCreateExpression(expr, false); - if (!id.has_value()) { - ENVOY_LOG(info, "Failed to parse expression: {}", expr); - } - const auto& tag_it = context_->all_tags_.find(tag); - if (tag_it == context_->all_tags_.end()) { - if (!id.has_value()) { - continue; - } - const auto tag_name = metric_overrides_->pool_.add(tag); - if (!metric.name().empty()) { - const auto& it = context_->all_metrics_.find(metric.name()); - if (it != context_->all_metrics_.end()) { - metric_overrides_->tag_additions_[it->second].push_back({tag_name, id.value()}); - } - const auto& custom_it = metric_overrides_->custom_metrics_.find(metric.name()); - if (custom_it != metric_overrides_->custom_metrics_.end()) { - metric_overrides_->tag_additions_[custom_it->second.name_].push_back( - {tag_name, id.value()}); - } - } else { - for (const auto& [_, metric] : context_->all_metrics_) { - metric_overrides_->tag_additions_[metric].push_back({tag_name, id.value()}); - } - for (const auto& [_, metric] : metric_overrides_->custom_metrics_) { - metric_overrides_->tag_additions_[metric.name_].push_back({tag_name, id.value()}); - } - } - } else { - const auto tag_name = tag_it->second; - if (!metric.name().empty()) { - const auto& it = context_->all_metrics_.find(metric.name()); - if (it != context_->all_metrics_.end()) { - metric_overrides_->tag_overrides_[it->second][tag_name] = id; - } - const auto& custom_it = metric_overrides_->custom_metrics_.find(metric.name()); - if (custom_it != metric_overrides_->custom_metrics_.end()) { - metric_overrides_->tag_additions_[custom_it->second.name_].push_back( - {tag_name, id.value()}); - } - } else { - for (const auto& [_, metric] : context_->all_metrics_) { - metric_overrides_->tag_overrides_[metric][tag_name] = id; - } - for (const auto& [_, metric] : metric_overrides_->custom_metrics_) { - metric_overrides_->tag_additions_[metric.name_].push_back({tag_name, id.value()}); - } - } - } - } - } - } - } - - // RAII for stream context propagation. - struct StreamOverrides : public Filters::Common::Expr::StreamActivation { - StreamOverrides(Config& parent, Stats::StatNameDynamicPool& pool) - : parent_(parent), pool_(pool) {} - - void evaluate(const StreamInfo::StreamInfo& info, - const Http::RequestHeaderMap* request_headers = nullptr, - const Http::ResponseHeaderMap* response_headers = nullptr, - const Http::ResponseTrailerMap* response_trailers = nullptr) { - evaluated_ = true; - if (parent_.metric_overrides_) { - local_info_ = &parent_.context_->local_info_; - activation_info_ = &info; - activation_request_headers_ = request_headers; - activation_response_headers_ = response_headers; - activation_response_trailers_ = response_trailers; - const auto& compiled_exprs = parent_.metric_overrides_->compiled_exprs_; - expr_values_.clear(); - expr_values_.reserve(compiled_exprs.size()); - for (size_t id = 0; id < compiled_exprs.size(); id++) { - Protobuf::Arena arena; - auto eval_status = compiled_exprs[id].first.evaluate(*this, &arena); - if (!eval_status.ok() || eval_status.value().IsError()) { - if (!eval_status.ok()) { - ENVOY_LOG(debug, "Failed to evaluate metric expression: {}", eval_status.status()); - } - if (eval_status.value().IsError()) { - ENVOY_LOG(debug, "Failed to evaluate metric expression: {}", - eval_status.value().ErrorOrDie()->message()); - } - expr_values_.push_back(std::make_pair(parent_.context_->unknown_, 0)); - } else { - const auto string_value = Filters::Common::Expr::print(eval_status.value()); - if (compiled_exprs[id].second) { - uint64_t amount = 0; - if (!absl::SimpleAtoi(string_value, &amount)) { - ENVOY_LOG(trace, "Failed to get metric value: {}", string_value); - } - expr_values_.push_back(std::make_pair(Stats::StatName(), amount)); - } else { - expr_values_.push_back(std::make_pair(pool_.add(string_value), 0)); - } - } - } - resetActivation(); - } - } - - void addCounter(Stats::StatName metric, const Stats::StatNameTagVector& tags, - uint64_t amount = 1) { - ASSERT(evaluated_); - if (parent_.metric_overrides_) { - if (parent_.metric_overrides_->drop_.contains(metric)) { - return; - } - auto new_tags = parent_.metric_overrides_->overrideTags(metric, tags, expr_values_); - Stats::Utility::counterFromStatNames(parent_.scope(), - {parent_.context_->stat_namespace_, metric}, new_tags) - .add(amount); - return; - } - Stats::Utility::counterFromStatNames(parent_.scope(), - {parent_.context_->stat_namespace_, metric}, tags) - .add(amount); - } - - void recordHistogram(Stats::StatName metric, Stats::Histogram::Unit unit, - const Stats::StatNameTagVector& tags, uint64_t value) { - ASSERT(evaluated_); - if (parent_.metric_overrides_) { - if (parent_.metric_overrides_->drop_.contains(metric)) { - return; - } - auto new_tags = parent_.metric_overrides_->overrideTags(metric, tags, expr_values_); - Stats::Utility::histogramFromStatNames( - parent_.scope(), {parent_.context_->stat_namespace_, metric}, unit, new_tags) - .recordValue(value); - return; - } - Stats::Utility::histogramFromStatNames( - parent_.scope(), {parent_.context_->stat_namespace_, metric}, unit, tags) - .recordValue(value); - } - - void recordCustomMetrics() { - ASSERT(evaluated_); - if (parent_.metric_overrides_) { - for (const auto& [_, metric] : parent_.metric_overrides_->custom_metrics_) { - const auto tags = parent_.metric_overrides_->overrideTags(metric.name_, {}, expr_values_); - uint64_t amount = expr_values_[metric.expr_].second; - switch (metric.type_) { - case MetricOverrides::MetricType::Counter: - Stats::Utility::counterFromStatNames( - parent_.scope(), {parent_.context_->stat_namespace_, metric.name_}, tags) - .add(amount); - break; - case MetricOverrides::MetricType::Histogram: - Stats::Utility::histogramFromStatNames( - parent_.scope(), {parent_.context_->stat_namespace_, metric.name_}, - Stats::Histogram::Unit::Bytes, tags) - .recordValue(amount); - break; - case MetricOverrides::MetricType::Gauge: - Stats::Utility::gaugeFromStatNames(parent_.scope(), - {parent_.context_->stat_namespace_, metric.name_}, - Stats::Gauge::ImportMode::Accumulate, tags) - .set(amount); - break; - default: - break; - } - } - } - } - - Config& parent_; - Stats::StatNameDynamicPool& pool_; - std::vector> expr_values_; - bool evaluated_{false}; - }; - - void recordVersion(Server::Configuration::FactoryContext& factory_context) { - Stats::StatNameTagVector tags; - tags.push_back({context_->component_, context_->proxy_}); - tags.push_back({context_->tag_, context_->istio_version_.empty() ? context_->unknown_ - : context_->istio_version_}); - - Stats::Utility::gaugeFromStatNames(factory_context.scope(), - {context_->stat_namespace_, context_->istio_build_}, - Stats::Gauge::ImportMode::Accumulate, tags) - .set(1); - } - - Reporter reporter() const { return reporter_; } - Stats::Scope& scope() { return *context_->scope_; } - - ContextSharedPtr context_; - Reporter reporter_; - - const bool disable_host_header_fallback_; - const std::chrono::milliseconds report_duration_; - std::unique_ptr metric_overrides_; -}; - -using ConfigSharedPtr = std::shared_ptr; - -class IstioStatsFilter : public Http::PassThroughFilter, - public Logger::Loggable, - public AccessLog::Instance, - public Network::ReadFilter, - public Network::ConnectionCallbacks { -public: - IstioStatsFilter(ConfigSharedPtr config) - : config_(config), context_(*config->context_), pool_(config->scope().symbolTable()), - stream_(*config_, pool_) { - tags_.reserve(25); - switch (config_->reporter()) { - case Reporter::ServerSidecar: - tags_.push_back({context_.reporter_, context_.destination_}); - break; - case Reporter::ServerGateway: - tags_.push_back({context_.reporter_, context_.waypoint_}); - break; - case Reporter::ClientSidecar: - tags_.push_back({context_.reporter_, context_.source_}); - break; - } - } - ~IstioStatsFilter() { ASSERT(report_timer_ == nullptr); } - - // Http::StreamDecoderFilter - Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap& request_headers, bool) override { - is_grpc_ = Grpc::Common::isGrpcRequestHeaders(request_headers); - if (is_grpc_) { - report_timer_ = decoder_callbacks_->dispatcher().createTimer([this] { onReportTimer(); }); - report_timer_->enableTimer(config_->report_duration_); - } - return Http::FilterHeadersStatus::Continue; - } - - // AccessLog::Instance - void log(const Formatter::Context& log_context, const StreamInfo::StreamInfo& info) override { - const Http::RequestHeaderMap* request_headers = log_context.requestHeaders().ptr(); - const Http::ResponseHeaderMap* response_headers = log_context.responseHeaders().ptr(); - const Http::ResponseTrailerMap* response_trailers = log_context.responseTrailers().ptr(); - - reportHelper(true); - if (is_grpc_) { - tags_.push_back({context_.request_protocol_, context_.grpc_}); - } else { - tags_.push_back({context_.request_protocol_, context_.http_}); - } - - // TODO: copy Http::CodeStatsImpl version for status codes and flags. - tags_.push_back( - {context_.response_code_, pool_.add(absl::StrCat(info.responseCode().value_or(0)))}); - if (is_grpc_) { - auto const& optional_status = Grpc::Common::getGrpcStatus( - response_trailers ? *response_trailers - : *Http::StaticEmptyHeaders::get().response_trailers, - response_headers ? *response_headers : *Http::StaticEmptyHeaders::get().response_headers, - info); - tags_.push_back( - {context_.grpc_response_status_, - optional_status ? pool_.add(absl::StrCat(optional_status.value())) : context_.empty_}); - } else { - tags_.push_back({context_.grpc_response_status_, context_.empty_}); - } - populateFlagsAndConnectionSecurity(info); - - // Evaluate the end stream override expressions for HTTP. This may change values for periodic - // metrics. - stream_.evaluate(info, request_headers, response_headers, response_trailers); - stream_.addCounter(context_.requests_total_, tags_); - auto duration = info.requestComplete(); - if (duration.has_value()) { - stream_.recordHistogram(context_.request_duration_milliseconds_, - Stats::Histogram::Unit::Milliseconds, tags_, - absl::FromChrono(duration.value()) / absl::Milliseconds(1)); - } - auto meter = info.getDownstreamBytesMeter(); - if (meter) { - stream_.recordHistogram(context_.request_bytes_, Stats::Histogram::Unit::Bytes, tags_, - meter->wireBytesReceived()); - stream_.recordHistogram(context_.response_bytes_, Stats::Histogram::Unit::Bytes, tags_, - meter->wireBytesSent()); - } - stream_.recordCustomMetrics(); - } - - // Network::ReadFilter - Network::FilterStatus onData(Buffer::Instance&, bool) override { - return Network::FilterStatus::Continue; - } - Network::FilterStatus onNewConnection() override { - if (config_->report_duration_ > std::chrono::milliseconds(0)) { - report_timer_ = network_read_callbacks_->connection().dispatcher().createTimer( - [this] { onReportTimer(); }); - report_timer_->enableTimer(config_->report_duration_); - } - return Network::FilterStatus::Continue; - } - void initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) override { - network_read_callbacks_ = &callbacks; - network_read_callbacks_->connection().addConnectionCallbacks(*this); - } - // Network::ConnectionCallbacks - void onEvent(Network::ConnectionEvent event) override { - switch (event) { - case Network::ConnectionEvent::LocalClose: - case Network::ConnectionEvent::RemoteClose: - reportHelper(true); - break; - default: - break; - } - } - void onAboveWriteBufferHighWatermark() override {} - void onBelowWriteBufferLowWatermark() override {} - -private: - // Invoked periodically for streams. - void reportHelper(bool end_stream) { - if (end_stream && report_timer_) { - report_timer_->disableTimer(); - report_timer_.reset(); - } - // HTTP handled first. - if (decoder_callbacks_) { - if (!peer_read_) { - const auto& info = decoder_callbacks_->streamInfo(); - peer_read_ = peerInfoRead(config_->reporter(), info.filterState()); - if (peer_read_ || end_stream) { - ENVOY_LOG(trace, "Populating peer metadata from HTTP MX."); - populatePeerInfo(info, info.filterState()); - } - if (is_grpc_ && (peer_read_ || end_stream)) { - // For periodic HTTP metric, evaluate once when the peer info is read. - stream_.evaluate(decoder_callbacks_->streamInfo()); - } - } - if (is_grpc_ && (peer_read_ || end_stream)) { - const auto* counters = - decoder_callbacks_->streamInfo() - .filterState() - ->getDataReadOnly("envoy.filters.http.grpc_stats"); - if (counters) { - stream_.addCounter(context_.request_messages_total_, tags_, - counters->request_message_count - request_message_count_); - stream_.addCounter(context_.response_messages_total_, tags_, - counters->response_message_count - response_message_count_); - request_message_count_ = counters->request_message_count; - response_message_count_ = counters->response_message_count; - } - } - return; - } - const auto& info = network_read_callbacks_->connection().streamInfo(); - // TCP MX writes to upstream stream info instead. - OptRef upstream_info; - if (config_->reporter() == Reporter::ClientSidecar) { - upstream_info = info.upstreamInfo(); - } - const StreamInfo::FilterState& filter_state = - upstream_info && upstream_info->upstreamFilterState() - ? *upstream_info->upstreamFilterState() - : info.filterState(); - - if (!peer_read_) { - peer_read_ = peerInfoRead(config_->reporter(), filter_state); - // Report connection open once peer info is read or connection is closed. - if (peer_read_ || end_stream) { - ENVOY_LOG(trace, "Populating peer metadata from TCP MX."); - populatePeerInfo(info, filter_state); - tags_.push_back({context_.request_protocol_, context_.tcp_}); - populateFlagsAndConnectionSecurity(info); - // For TCP, evaluate only once immediately before emitting the first metric. - stream_.evaluate(info); - stream_.addCounter(context_.tcp_connections_opened_total_, tags_); - } - } - if (peer_read_ || end_stream) { - auto meter = info.getDownstreamBytesMeter(); - if (meter) { - stream_.addCounter(context_.tcp_sent_bytes_total_, tags_, - meter->wireBytesSent() - bytes_sent_); - bytes_sent_ = meter->wireBytesSent(); - stream_.addCounter(context_.tcp_received_bytes_total_, tags_, - meter->wireBytesReceived() - bytes_received_); - bytes_received_ = meter->wireBytesReceived(); - } - } - if (end_stream) { - stream_.addCounter(context_.tcp_connections_closed_total_, tags_); - stream_.recordCustomMetrics(); - } - } - void onReportTimer() { - reportHelper(false); - report_timer_->enableTimer(config_->report_duration_); - } - - void populateFlagsAndConnectionSecurity(const StreamInfo::StreamInfo& info) { - tags_.push_back( - {context_.response_flags_, pool_.add(StreamInfo::ResponseFlagUtils::toShortString(info))}); - tags_.push_back({context_.connection_security_policy_, - mutual_tls_.has_value() - ? (*mutual_tls_ ? context_.mutual_tls_ : context_.none_) - : context_.unknown_}); - } - - // Peer metadata is populated after encode/decodeHeaders by MX HTTP filter, - // and after initial bytes read/written by MX TCP filter. - void populatePeerInfo(const StreamInfo::StreamInfo& info, - const StreamInfo::FilterState& filter_state) { - // Compute peer info with client-side fallbacks. - absl::optional peer; - auto object = peerInfo(config_->reporter(), filter_state); - if (object) { - peer.emplace(object.value()); - } else if (config_->reporter() == Reporter::ClientSidecar) { - if (auto label_obj = extractEndpointMetadata(info); label_obj) { - peer.emplace(label_obj.value()); - } - } - - // Compute destination service with client-side fallbacks. - absl::string_view service_host; - absl::string_view service_host_name; - absl::string_view service_namespace; - if (!config_->disable_host_header_fallback_) { - const auto* headers = info.getRequestHeaders(); - if (headers && headers->Host()) { - service_host = headers->Host()->value().getStringView(); - service_host_name = service_host; - } - } - if (info.getRouteName() == "block_all") { - service_host_name = "BlackHoleCluster"; - } else if (info.getRouteName() == "allow_any") { - service_host_name = "PassthroughCluster"; - } else { - const auto cluster_info = info.upstreamClusterInfo(); - if (cluster_info && cluster_info.value()) { - const auto& cluster_name = cluster_info.value()->name(); - if (cluster_name == "BlackHoleCluster" || cluster_name == "PassthroughCluster" || - cluster_name == "InboundPassthroughCluster" || - cluster_name == "InboundPassthroughClusterIpv4" || - cluster_name == "InboundPassthroughClusterIpv6") { - service_host_name = cluster_name; - } else { - const auto& filter_metadata = cluster_info.value()->metadata().filter_metadata(); - const auto& it = filter_metadata.find("istio"); - if (it != filter_metadata.end()) { - const auto& services_it = it->second.fields().find("services"); - if (services_it != it->second.fields().end()) { - const auto& services = services_it->second.list_value(); - if (services.values_size() > 0) { - const auto& service = services.values(0).struct_value().fields(); - const auto& host_it = service.find("host"); - if (host_it != service.end()) { - service_host = host_it->second.string_value(); - } - const auto& name_it = service.find("name"); - const auto& namespace_it = service.find("namespace"); - if (namespace_it != service.end()) { - service_namespace = namespace_it->second.string_value(); - } - if (name_it != service.end()) { - service_host_name = name_it->second.string_value(); - } else { - service_host_name = service_host.substr(0, service_host.find_first_of('.')); - } - } - } - } - } - } - } - - std::string peer_san; - absl::string_view local_san; - switch (config_->reporter()) { - case Reporter::ServerSidecar: - case Reporter::ServerGateway: { - auto peer_principal = - info.filterState().getDataReadOnly("io.istio.peer_principal"); - auto local_principal = - info.filterState().getDataReadOnly("io.istio.local_principal"); - peer_san = peer_principal ? peer_principal->asString() : ""; - local_san = local_principal ? local_principal->asString() : ""; - - // This fallback should be deleted once istio_authn is globally enabled. - if (peer_san.empty() && local_san.empty()) { - const Ssl::ConnectionInfoConstSharedPtr ssl_info = - info.downstreamAddressProvider().sslConnection(); - if (ssl_info && !ssl_info->uriSanPeerCertificate().empty()) { - peer_san = ssl_info->uriSanPeerCertificate()[0]; - } - if (ssl_info && !ssl_info->uriSanLocalCertificate().empty()) { - local_san = ssl_info->uriSanLocalCertificate()[0]; - } - } - - // Save the connection security policy for a tag added later. - mutual_tls_ = !peer_san.empty() && !local_san.empty(); - break; - } - case Reporter::ClientSidecar: { - const Ssl::ConnectionInfoConstSharedPtr ssl_info = - info.upstreamInfo() ? info.upstreamInfo()->upstreamSslConnection() : nullptr; - std::optional endpoint_peer; - if (ssl_info && !ssl_info->uriSanPeerCertificate().empty()) { - peer_san = ssl_info->uriSanPeerCertificate()[0]; - } - if (peer_san.empty()) { - auto endpoint_object = peerInfo(config_->reporter(), filter_state); - if (endpoint_object) { - endpoint_peer.emplace(endpoint_object.value()); - peer_san = endpoint_peer->identity_; - } - } - // This won't work for sidecar/ingress -> ambient becuase of the CONNECT - // tunnel. - if (ssl_info && !ssl_info->uriSanLocalCertificate().empty()) { - local_san = ssl_info->uriSanLocalCertificate()[0]; - } - break; - } - } - // Implements fallback from using the namespace from SAN if available to - // using peer metadata, otherwise. - absl::string_view peer_namespace; - if (!peer_san.empty()) { - const auto san_namespace = getNamespace(peer_san); - if (san_namespace) { - peer_namespace = san_namespace.value(); - } - } - if (peer_namespace.empty() && peer) { - peer_namespace = peer->namespace_name_; - } - switch (config_->reporter()) { - case Reporter::ServerSidecar: - case Reporter::ServerGateway: { - tags_.push_back({context_.source_workload_, peer && !peer->workload_name_.empty() - ? pool_.add(peer->workload_name_) - : context_.unknown_}); - tags_.push_back({context_.source_canonical_service_, peer && !peer->canonical_name_.empty() - ? pool_.add(peer->canonical_name_) - : context_.unknown_}); - tags_.push_back( - {context_.source_canonical_revision_, peer && !peer->canonical_revision_.empty() - ? pool_.add(peer->canonical_revision_) - : context_.latest_}); - tags_.push_back({context_.source_workload_namespace_, - !peer_namespace.empty() ? pool_.add(peer_namespace) : context_.unknown_}); - tags_.push_back({context_.source_principal_, - !peer_san.empty() ? pool_.add(peer_san) : context_.unknown_}); - tags_.push_back({context_.source_app_, peer && !peer->app_name_.empty() - ? pool_.add(peer->app_name_) - : context_.unknown_}); - tags_.push_back({context_.source_version_, peer && !peer->app_version_.empty() - ? pool_.add(peer->app_version_) - : context_.unknown_}); - tags_.push_back({context_.source_cluster_, peer && !peer->cluster_name_.empty() - ? pool_.add(peer->cluster_name_) - : context_.unknown_}); - switch (config_->reporter()) { - case Reporter::ServerGateway: { - std::optional endpoint_peer; - auto endpoint_object = peerInfo(Reporter::ClientSidecar, filter_state); - if (endpoint_object) { - endpoint_peer.emplace(endpoint_object.value()); - } - tags_.push_back( - {context_.destination_workload_, endpoint_peer && !endpoint_peer->workload_name_.empty() - ? pool_.add(endpoint_peer->workload_name_) - : context_.unknown_}); - tags_.push_back({context_.destination_workload_namespace_, - endpoint_peer && !endpoint_peer->namespace_name_.empty() - ? pool_.add(endpoint_peer->namespace_name_) - : context_.unknown_}); - tags_.push_back( - {context_.destination_principal_, endpoint_peer && !endpoint_peer->identity_.empty() - ? pool_.add(endpoint_peer->identity_) - : context_.unknown_}); - // Endpoint encoding does not have app and version. - tags_.push_back( - {context_.destination_app_, endpoint_peer && !endpoint_peer->app_name_.empty() - ? pool_.add(endpoint_peer->app_name_) - : context_.unknown_}); - tags_.push_back( - {context_.destination_version_, endpoint_peer && !endpoint_peer->app_version_.empty() - ? pool_.add(endpoint_peer->app_version_) - : context_.unknown_}); - tags_.push_back({context_.destination_service_, - service_host.empty() ? context_.unknown_ : pool_.add(service_host)}); - tags_.push_back({context_.destination_canonical_service_, - endpoint_peer && !endpoint_peer->canonical_name_.empty() - ? pool_.add(endpoint_peer->canonical_name_) - : context_.unknown_}); - tags_.push_back({context_.destination_canonical_revision_, - endpoint_peer && !endpoint_peer->canonical_revision_.empty() - ? pool_.add(endpoint_peer->canonical_revision_) - : context_.unknown_}); - tags_.push_back({context_.destination_service_name_, service_host_name.empty() - ? context_.unknown_ - : pool_.add(service_host_name)}); - tags_.push_back({context_.destination_service_namespace_, !service_namespace.empty() - ? pool_.add(service_namespace) - : context_.unknown_}); - tags_.push_back( - {context_.destination_cluster_, endpoint_peer && !endpoint_peer->cluster_name_.empty() - ? pool_.add(endpoint_peer->cluster_name_) - : context_.unknown_}); - break; - } - default: - tags_.push_back({context_.destination_workload_, context_.workload_name_}); - tags_.push_back({context_.destination_workload_namespace_, context_.namespace_}); - tags_.push_back({context_.destination_principal_, - !local_san.empty() ? pool_.add(local_san) : context_.unknown_}); - tags_.push_back({context_.destination_app_, context_.app_name_}); - tags_.push_back({context_.destination_version_, context_.app_version_}); - tags_.push_back({context_.destination_service_, service_host.empty() - ? context_.canonical_name_ - : pool_.add(service_host)}); - tags_.push_back({context_.destination_canonical_service_, context_.canonical_name_}); - tags_.push_back({context_.destination_canonical_revision_, context_.canonical_revision_}); - tags_.push_back({context_.destination_service_name_, service_host_name.empty() - ? context_.canonical_name_ - : pool_.add(service_host_name)}); - tags_.push_back({context_.destination_service_namespace_, context_.namespace_}); - tags_.push_back({context_.destination_cluster_, context_.cluster_name_}); - break; - } - - break; - } - case Reporter::ClientSidecar: { - tags_.push_back({context_.source_workload_, context_.workload_name_}); - tags_.push_back({context_.source_canonical_service_, context_.canonical_name_}); - tags_.push_back({context_.source_canonical_revision_, context_.canonical_revision_}); - tags_.push_back({context_.source_workload_namespace_, context_.namespace_}); - tags_.push_back({context_.source_principal_, - !local_san.empty() ? pool_.add(local_san) : context_.unknown_}); - tags_.push_back({context_.source_app_, context_.app_name_}); - tags_.push_back({context_.source_version_, context_.app_version_}); - tags_.push_back({context_.source_cluster_, context_.cluster_name_}); - tags_.push_back({context_.destination_workload_, peer && !peer->workload_name_.empty() - ? pool_.add(peer->workload_name_) - : context_.unknown_}); - tags_.push_back({context_.destination_workload_namespace_, - !peer_namespace.empty() ? pool_.add(peer_namespace) : context_.unknown_}); - tags_.push_back({context_.destination_principal_, - !peer_san.empty() ? pool_.add(peer_san) : context_.unknown_}); - tags_.push_back({context_.destination_app_, peer && !peer->app_name_.empty() - ? pool_.add(peer->app_name_) - : context_.unknown_}); - tags_.push_back({context_.destination_version_, peer && !peer->app_version_.empty() - ? pool_.add(peer->app_version_) - : context_.unknown_}); - tags_.push_back({context_.destination_service_, - service_host.empty() ? context_.unknown_ : pool_.add(service_host)}); - tags_.push_back({context_.destination_canonical_service_, - peer && !peer->canonical_name_.empty() ? pool_.add(peer->canonical_name_) - : context_.unknown_}); - tags_.push_back( - {context_.destination_canonical_revision_, peer && !peer->canonical_revision_.empty() - ? pool_.add(peer->canonical_revision_) - : context_.latest_}); - tags_.push_back({context_.destination_service_name_, service_host_name.empty() - ? context_.unknown_ - : pool_.add(service_host_name)}); - tags_.push_back( - {context_.destination_service_namespace_, - !service_namespace.empty() - ? pool_.add(service_namespace) - : (!peer_namespace.empty() ? pool_.add(peer_namespace) : context_.unknown_)}); - tags_.push_back({context_.destination_cluster_, peer && !peer->cluster_name_.empty() - ? pool_.add(peer->cluster_name_) - : context_.unknown_}); - break; - } - default: - break; - } - } - - ConfigSharedPtr config_; - Context& context_; - Stats::StatNameDynamicPool pool_; - Stats::StatNameTagVector tags_; - Event::TimerPtr report_timer_{nullptr}; - Network::ReadFilterCallbacks* network_read_callbacks_; - bool peer_read_{false}; - uint64_t bytes_sent_{0}; - uint64_t bytes_received_{0}; - absl::optional mutual_tls_; - bool is_grpc_{false}; - uint64_t request_message_count_{0}; - uint64_t response_message_count_{0}; - // Custom expression values are evaluated at most twice: at the start and the end of the stream. - Config::StreamOverrides stream_; -}; - -} // namespace - -absl::StatusOr IstioStatsFilterConfigFactory::createFilterFactoryFromProto( - const Protobuf::Message& proto_config, const std::string&, - Server::Configuration::FactoryContext& factory_context) { - factory_context.serverFactoryContext().api().customStatNamespaces().registerStatNamespace( - CustomStatNamespace); - ConfigSharedPtr config = std::make_shared( - dynamic_cast(proto_config), factory_context); - return [config](Http::FilterChainFactoryCallbacks& callbacks) { - auto filter = std::make_shared(config); - callbacks.addStreamFilter(filter); - // Wasm filters inject filter state in access log handlers, which are called - // after onStreamComplete. - callbacks.addAccessLogHandler(filter); - }; -} - -REGISTER_FACTORY(IstioStatsFilterConfigFactory, - Server::Configuration::NamedHttpFilterConfigFactory); - -absl::StatusOr -IstioStatsNetworkFilterConfigFactory::createFilterFactoryFromProto( - const Protobuf::Message& proto_config, Server::Configuration::FactoryContext& factory_context) { - factory_context.serverFactoryContext().api().customStatNamespaces().registerStatNamespace( - CustomStatNamespace); - ConfigSharedPtr config = std::make_shared( - dynamic_cast(proto_config), factory_context); - return [config](Network::FilterManager& filter_manager) { - filter_manager.addReadFilter(std::make_shared(config)); - }; -} - -REGISTER_FACTORY(IstioStatsNetworkFilterConfigFactory, - Server::Configuration::NamedNetworkFilterConfigFactory); - -} // namespace IstioStats -} // namespace HttpFilters -} // namespace Extensions -} // namespace Envoy diff --git a/source/extensions/filters/http/istio_stats/istio_stats.h b/source/extensions/filters/http/istio_stats/istio_stats.h deleted file mode 100644 index fe0af8ce831..00000000000 --- a/source/extensions/filters/http/istio_stats/istio_stats.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include "envoy/server/filter_config.h" -#include "envoy/stream_info/filter_state.h" -#include "source/extensions/filters/http/istio_stats/config.pb.h" - -namespace Envoy { -namespace Extensions { -namespace HttpFilters { -namespace IstioStats { - -class IstioStatsFilterConfigFactory : public Server::Configuration::NamedHttpFilterConfigFactory { -public: - std::string name() const override { return "envoy.filters.http.istio_stats"; } - - ProtobufTypes::MessagePtr createEmptyConfigProto() override { - return std::make_unique(); - } - - absl::StatusOr - createFilterFactoryFromProto(const Protobuf::Message& proto_config, const std::string&, - Server::Configuration::FactoryContext&) override; -}; - -class IstioStatsNetworkFilterConfigFactory - : public Server::Configuration::NamedNetworkFilterConfigFactory { -public: - std::string name() const override { return "envoy.filters.network.istio_stats"; } - - ProtobufTypes::MessagePtr createEmptyConfigProto() override { - return std::make_unique(); - } - - absl::StatusOr - createFilterFactoryFromProto(const Protobuf::Message& proto_config, - Server::Configuration::FactoryContext& factory_context) override; -}; - -} // namespace IstioStats -} // namespace HttpFilters -} // namespace Extensions -} // namespace Envoy diff --git a/source/extensions/filters/http/peer_metadata/BUILD b/source/extensions/filters/http/peer_metadata/BUILD deleted file mode 100644 index 380ec034e7b..00000000000 --- a/source/extensions/filters/http/peer_metadata/BUILD +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright 2018 Istio Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ -# - -load( - "@envoy//bazel:envoy_build_system.bzl", - "envoy_cc_library", - "envoy_cc_test", - "envoy_proto_library", -) - -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -envoy_cc_library( - name = "filter_lib", - srcs = ["filter.cc"], - hdrs = ["filter.h"], - repository = "@envoy", - deps = [ - ":config_cc_proto", - "//extensions/common:metadata_object_lib", - "//source/extensions/common/workload_discovery:api_lib", - "@envoy//envoy/registry", - "@envoy//source/common/common:base64_lib", - "@envoy//source/common/common:hash_lib", - "@envoy//source/common/http:header_utility_lib", - "@envoy//source/common/http:utility_lib", - "@envoy//source/common/network:utility_lib", - "@envoy//source/extensions/filters/common/expr:cel_state_lib", - "@envoy//source/extensions/filters/http/common:factory_base_lib", - "@envoy//source/extensions/filters/http/common:pass_through_filter_lib", - ], -) - -envoy_proto_library( - name = "config", - srcs = ["config.proto"], -) - -envoy_cc_test( - name = "filter_test", - srcs = ["filter_test.cc"], - repository = "@envoy", - deps = [ - ":filter_lib", - "@envoy//source/common/network:address_lib", - "@envoy//test/common/stream_info:test_util", - "@envoy//test/mocks/server:factory_context_mocks", - "@envoy//test/mocks/stream_info:stream_info_mocks", - "@envoy//test/test_common:logging_lib", - ], -) diff --git a/source/extensions/filters/http/peer_metadata/config.proto b/source/extensions/filters/http/peer_metadata/config.proto deleted file mode 100644 index 04e81f4f6b5..00000000000 --- a/source/extensions/filters/http/peer_metadata/config.proto +++ /dev/null @@ -1,82 +0,0 @@ -/* Copyright Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto3"; - -package io.istio.http.peer_metadata; - -// Peer metadata provider filter. This filter encapsulates the discovery of the -// peer telemetry attributes for consumption by the telemetry filters. -message Config { - // DEPRECATED. - // This method uses `baggage` header encoding. - message Baggage { - } - - // This method uses the workload metadata xDS. Requires that the bootstrap extension is enabled. - // For downstream discovery, the remote address is the lookup key in xDS. - // For upstream discovery: - // - // * If the upstream host address is an IP, this IP is used as the lookup key; - // - // * If the upstream host address is internal, uses the - // "filter_metadata.tunnel.destination" dynamic metadata value as the lookup key. - message WorkloadDiscovery { - } - - // This method uses Istio HTTP metadata exchange headers, e.g. `x-envoy-peer-metadata`. Removes these headers if found. - message IstioHeaders { - // Strip x-envoy-peer-metadata and x-envoy-peer-metadata-id headers on HTTP requests to services outside the mesh. - // Detects upstream clusters with `istio` and `external` filter metadata fields - bool skip_external_clusters = 1; - } - - // An exhaustive list of the derivation methods. - message DiscoveryMethod { - oneof method_specifier { - Baggage baggage = 1; - WorkloadDiscovery workload_discovery = 2; - IstioHeaders istio_headers = 3; - } - } - - // The order of the derivation of the downstream peer metadata, in the precedence order. - // First successful lookup wins. - repeated DiscoveryMethod downstream_discovery = 1; - - // The order of the derivation of the upstream peer metadata, in the precedence order. - // First successful lookup wins. - repeated DiscoveryMethod upstream_discovery = 2; - - // An exhaustive list of the metadata propagation methods. - message PropagationMethod { - oneof method_specifier { - IstioHeaders istio_headers = 1; - } - } - - // Downstream injection of the metadata via a response header. - repeated PropagationMethod downstream_propagation = 3; - - // Upstream injection of the metadata via a request header. - repeated PropagationMethod upstream_propagation = 4; - - // True to enable sharing with the upstream. - bool shared_with_upstream = 5; - - // Additional labels to be added to the peer metadata to help your understand the traffic. - // e.g. `role`, `location` etc. - repeated string additional_labels = 6; -} diff --git a/source/extensions/filters/http/peer_metadata/filter.cc b/source/extensions/filters/http/peer_metadata/filter.cc deleted file mode 100644 index f4b93cad446..00000000000 --- a/source/extensions/filters/http/peer_metadata/filter.cc +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "source/extensions/filters/http/peer_metadata/filter.h" - -#include "envoy/registry/registry.h" -#include "envoy/server/factory_context.h" -#include "source/common/common/hash.h" -#include "source/common/common/base64.h" -#include "source/common/http/header_utility.h" -#include "source/common/http/utility.h" -#include "source/common/network/utility.h" - -#include "extensions/common/metadata_object.h" - -namespace Envoy { -namespace Extensions { -namespace HttpFilters { -namespace PeerMetadata { - -using ::Envoy::Extensions::Filters::Common::Expr::CelState; - -class XDSMethod : public DiscoveryMethod { -public: - XDSMethod(bool downstream, Server::Configuration::ServerFactoryContext& factory_context) - : downstream_(downstream), - metadata_provider_(Extensions::Common::WorkloadDiscovery::GetProvider(factory_context)) {} - absl::optional derivePeerInfo(const StreamInfo::StreamInfo&, Http::HeaderMap&, - Context&) const override; - -private: - const bool downstream_; - Extensions::Common::WorkloadDiscovery::WorkloadMetadataProviderSharedPtr metadata_provider_; -}; - -absl::optional XDSMethod::derivePeerInfo(const StreamInfo::StreamInfo& info, - Http::HeaderMap&, Context&) const { - if (!metadata_provider_) { - return {}; - } - Network::Address::InstanceConstSharedPtr peer_address; - if (downstream_) { - peer_address = info.downstreamAddressProvider().remoteAddress(); - } else { - if (info.upstreamInfo().has_value()) { - auto upstream_host = info.upstreamInfo().value().get().upstreamHost(); - if (upstream_host) { - const auto address = upstream_host->address(); - switch (address->type()) { - case Network::Address::Type::Ip: - peer_address = upstream_host->address(); - break; - case Network::Address::Type::EnvoyInternal: - if (upstream_host->metadata()) { - const auto& filter_metadata = upstream_host->metadata()->filter_metadata(); - const auto& it = filter_metadata.find("envoy.filters.listener.original_dst"); - if (it != filter_metadata.end()) { - const auto& destination_it = it->second.fields().find("local"); - if (destination_it != it->second.fields().end()) { - peer_address = Network::Utility::parseInternetAddressAndPortNoThrow( - destination_it->second.string_value(), /*v6only=*/false); - } - } - } - break; - default: - break; - } - } - } - } - if (!peer_address) { - return {}; - } - ENVOY_LOG_MISC(debug, "Peer address: {}", peer_address->asString()); - return metadata_provider_->GetMetadata(peer_address); -} - -MXMethod::MXMethod(bool downstream, const absl::flat_hash_set additional_labels, - Server::Configuration::ServerFactoryContext& factory_context) - : downstream_(downstream), tls_(factory_context.threadLocal()), - additional_labels_(additional_labels) { - tls_.set([](Event::Dispatcher&) { return std::make_shared(); }); -} - -absl::optional MXMethod::derivePeerInfo(const StreamInfo::StreamInfo&, - Http::HeaderMap& headers, Context& ctx) const { - const auto peer_id_header = headers.get(Headers::get().ExchangeMetadataHeaderId); - if (downstream_) { - ctx.request_peer_id_received_ = !peer_id_header.empty(); - } - absl::string_view peer_id = - peer_id_header.empty() ? "" : peer_id_header[0]->value().getStringView(); - const auto peer_info_header = headers.get(Headers::get().ExchangeMetadataHeader); - if (downstream_) { - ctx.request_peer_received_ = !peer_info_header.empty(); - } - absl::string_view peer_info = - peer_info_header.empty() ? "" : peer_info_header[0]->value().getStringView(); - if (!peer_info.empty()) { - return lookup(peer_id, peer_info); - } - return {}; -} - -void MXMethod::remove(Http::HeaderMap& headers) const { - headers.remove(Headers::get().ExchangeMetadataHeaderId); - headers.remove(Headers::get().ExchangeMetadataHeader); -} - -absl::optional MXMethod::lookup(absl::string_view id, absl::string_view value) const { - // This code is copied from: - // https://github.com/istio/proxy/blob/release-1.18/extensions/metadata_exchange/plugin.cc#L116 - auto& cache = tls_->cache_; - if (max_peer_cache_size_ > 0 && !id.empty()) { - auto it = cache.find(id); - if (it != cache.end()) { - return it->second; - } - } - const auto bytes = Base64::decodeWithoutPadding(value); - google::protobuf::Struct metadata; - if (!metadata.ParseFromString(bytes)) { - return {}; - } - auto out = Istio::Common::convertStructToWorkloadMetadata(metadata, additional_labels_); - if (max_peer_cache_size_ > 0 && !id.empty()) { - // do not let the cache grow beyond max cache size. - if (static_cast(cache.size()) > max_peer_cache_size_) { - cache.erase(cache.begin(), std::next(cache.begin(), max_peer_cache_size_ / 4)); - } - cache.emplace(id, *out); - } - return *out; -} - -MXPropagationMethod::MXPropagationMethod( - bool downstream, Server::Configuration::ServerFactoryContext& factory_context, - const absl::flat_hash_set& additional_labels, - const io::istio::http::peer_metadata::Config_IstioHeaders& istio_headers) - : downstream_(downstream), id_(factory_context.localInfo().node().id()), - value_(computeValue(additional_labels, factory_context)), - skip_external_clusters_(istio_headers.skip_external_clusters()) {} - -std::string MXPropagationMethod::computeValue( - const absl::flat_hash_set& additional_labels, - Server::Configuration::ServerFactoryContext& factory_context) const { - const auto obj = Istio::Common::convertStructToWorkloadMetadata( - factory_context.localInfo().node().metadata(), additional_labels); - const google::protobuf::Struct metadata = Istio::Common::convertWorkloadMetadataToStruct(*obj); - const std::string metadata_bytes = Istio::Common::serializeToStringDeterministic(metadata); - return Base64::encode(metadata_bytes.data(), metadata_bytes.size()); -} - -void MXPropagationMethod::inject(const StreamInfo::StreamInfo& info, Http::HeaderMap& headers, - Context& ctx) const { - if (skipMXHeaders(skip_external_clusters_, info)) { - return; - } - if (!downstream_ || ctx.request_peer_id_received_) { - headers.setReference(Headers::get().ExchangeMetadataHeaderId, id_); - } - if (!downstream_ || ctx.request_peer_received_) { - headers.setReference(Headers::get().ExchangeMetadataHeader, value_); - } -} - -FilterConfig::FilterConfig(const io::istio::http::peer_metadata::Config& config, - Server::Configuration::FactoryContext& factory_context) - : shared_with_upstream_(config.shared_with_upstream()), - downstream_discovery_(buildDiscoveryMethods(config.downstream_discovery(), - buildAdditionalLabels(config.additional_labels()), - true, factory_context)), - upstream_discovery_(buildDiscoveryMethods(config.upstream_discovery(), - buildAdditionalLabels(config.additional_labels()), - false, factory_context)), - downstream_propagation_(buildPropagationMethods( - config.downstream_propagation(), buildAdditionalLabels(config.additional_labels()), true, - factory_context)), - upstream_propagation_(buildPropagationMethods( - config.upstream_propagation(), buildAdditionalLabels(config.additional_labels()), false, - factory_context)) {} - -std::vector FilterConfig::buildDiscoveryMethods( - const Protobuf::RepeatedPtrField& - config, - const absl::flat_hash_set& additional_labels, bool downstream, - Server::Configuration::FactoryContext& factory_context) const { - std::vector methods; - methods.reserve(config.size()); - for (const auto& method : config) { - switch (method.method_specifier_case()) { - case io::istio::http::peer_metadata::Config::DiscoveryMethod::MethodSpecifierCase:: - kWorkloadDiscovery: - methods.push_back( - std::make_unique(downstream, factory_context.serverFactoryContext())); - break; - case io::istio::http::peer_metadata::Config::DiscoveryMethod::MethodSpecifierCase:: - kIstioHeaders: - methods.push_back(std::make_unique(downstream, additional_labels, - factory_context.serverFactoryContext())); - break; - default: - break; - } - } - return methods; -} - -std::vector FilterConfig::buildPropagationMethods( - const Protobuf::RepeatedPtrField& - config, - const absl::flat_hash_set& additional_labels, bool downstream, - Server::Configuration::FactoryContext& factory_context) const { - std::vector methods; - methods.reserve(config.size()); - for (const auto& method : config) { - switch (method.method_specifier_case()) { - case io::istio::http::peer_metadata::Config::PropagationMethod::MethodSpecifierCase:: - kIstioHeaders: - methods.push_back( - std::make_unique(downstream, factory_context.serverFactoryContext(), - additional_labels, method.istio_headers())); - break; - default: - break; - } - } - return methods; -} - -absl::flat_hash_set -FilterConfig::buildAdditionalLabels(const Protobuf::RepeatedPtrField& labels) const { - absl::flat_hash_set result; - for (const auto& label : labels) { - result.emplace(label); - } - return result; -} - -void FilterConfig::discoverDownstream(StreamInfo::StreamInfo& info, Http::RequestHeaderMap& headers, - Context& ctx) const { - discover(info, true, headers, ctx); -} - -void FilterConfig::discoverUpstream(StreamInfo::StreamInfo& info, Http::ResponseHeaderMap& headers, - Context& ctx) const { - discover(info, false, headers, ctx); -} - -void FilterConfig::discover(StreamInfo::StreamInfo& info, bool downstream, Http::HeaderMap& headers, - Context& ctx) const { - for (const auto& method : downstream ? downstream_discovery_ : upstream_discovery_) { - const auto result = method->derivePeerInfo(info, headers, ctx); - if (result) { - setFilterState(info, downstream, *result); - break; - } - } - for (const auto& method : downstream ? downstream_discovery_ : upstream_discovery_) { - method->remove(headers); - } -} - -void FilterConfig::injectDownstream(const StreamInfo::StreamInfo& info, - Http::ResponseHeaderMap& headers, Context& ctx) const { - for (const auto& method : downstream_propagation_) { - method->inject(info, headers, ctx); - } -} - -void FilterConfig::injectUpstream(const StreamInfo::StreamInfo& info, - Http::RequestHeaderMap& headers, Context& ctx) const { - for (const auto& method : upstream_propagation_) { - method->inject(info, headers, ctx); - } -} - -void FilterConfig::setFilterState(StreamInfo::StreamInfo& info, bool downstream, - const PeerInfo& value) const { - const absl::string_view key = - downstream ? Istio::Common::DownstreamPeer : Istio::Common::UpstreamPeer; - if (!info.filterState()->hasDataWithName(key)) { - // Use CelState to allow operation filter_state.upstream_peer.labels['role'] - auto pb = value.serializeAsProto(); - auto peer_info = std::make_unique(FilterConfig::peerInfoPrototype()); - peer_info->setValue(absl::string_view(pb->SerializeAsString())); - info.filterState()->setData( - key, std::move(peer_info), StreamInfo::FilterState::StateType::Mutable, - StreamInfo::FilterState::LifeSpan::FilterChain, sharedWithUpstream()); - } else { - ENVOY_LOG(debug, "Duplicate peer metadata, skipping"); - } -} - -Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, bool) { - config_->discoverDownstream(decoder_callbacks_->streamInfo(), headers, ctx_); - config_->injectUpstream(decoder_callbacks_->streamInfo(), headers, ctx_); - return Http::FilterHeadersStatus::Continue; -} - -bool MXPropagationMethod::skipMXHeaders(const bool skip_external_clusters, - const StreamInfo::StreamInfo& info) const { - // We skip metadata in two cases. - // 1. skip_external_clusters is enabled, and we detect the upstream as external. - const auto& cluster_info = info.upstreamClusterInfo(); - if (cluster_info && cluster_info.value()) { - const auto& cluster_name = cluster_info.value()->name(); - // PassthroughCluster is always considered external - if (skip_external_clusters && cluster_name == "PassthroughCluster") { - return true; - } - const auto& filter_metadata = cluster_info.value()->metadata().filter_metadata(); - const auto& it = filter_metadata.find("istio"); - // Otherwise, cluster must be tagged as external - if (it != filter_metadata.end()) { - if (skip_external_clusters) { - const auto& skip_mx = it->second.fields().find("external"); - if (skip_mx != it->second.fields().end()) { - if (skip_mx->second.bool_value()) { - return true; - } - } - } - const auto& skip_mx = it->second.fields().find("disable_mx"); - if (skip_mx != it->second.fields().end()) { - if (skip_mx->second.bool_value()) { - return true; - } - } - } - } - return false; -} - -Http::FilterHeadersStatus Filter::encodeHeaders(Http::ResponseHeaderMap& headers, bool) { - config_->discoverUpstream(decoder_callbacks_->streamInfo(), headers, ctx_); - config_->injectDownstream(decoder_callbacks_->streamInfo(), headers, ctx_); - return Http::FilterHeadersStatus::Continue; -} - -absl::StatusOr FilterConfigFactory::createFilterFactoryFromProto( - const Protobuf::Message& config, const std::string&, - Server::Configuration::FactoryContext& factory_context) { - auto filter_config = std::make_shared( - dynamic_cast(config), factory_context); - return [filter_config](Http::FilterChainFactoryCallbacks& callbacks) { - auto filter = std::make_shared(filter_config); - callbacks.addStreamFilter(filter); - }; -} - -REGISTER_FACTORY(FilterConfigFactory, Server::Configuration::NamedHttpFilterConfigFactory); - -} // namespace PeerMetadata -} // namespace HttpFilters -} // namespace Extensions -} // namespace Envoy diff --git a/source/extensions/filters/http/peer_metadata/filter.h b/source/extensions/filters/http/peer_metadata/filter.h deleted file mode 100644 index 94da2a86c83..00000000000 --- a/source/extensions/filters/http/peer_metadata/filter.h +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include "source/extensions/filters/common/expr/cel_state.h" -#include "source/extensions/filters/http/common/factory_base.h" -#include "source/extensions/filters/http/common/pass_through_filter.h" -#include "source/extensions/filters/http/peer_metadata/config.pb.h" -#include "source/extensions/common/workload_discovery/api.h" -#include "source/common/singleton/const_singleton.h" - -namespace Envoy { -namespace Extensions { -namespace HttpFilters { -namespace PeerMetadata { - -using ::Envoy::Extensions::Filters::Common::Expr::CelStatePrototype; -using ::Envoy::Extensions::Filters::Common::Expr::CelStateType; - -struct HeaderValues { - const Http::LowerCaseString ExchangeMetadataHeader{"x-envoy-peer-metadata"}; - const Http::LowerCaseString ExchangeMetadataHeaderId{"x-envoy-peer-metadata-id"}; -}; - -using Headers = ConstSingleton; - -using PeerInfo = Istio::Common::WorkloadMetadataObject; - -struct Context { - bool request_peer_id_received_{false}; - bool request_peer_received_{false}; -}; - -// Base class for the discovery methods. First derivation wins but all methods perform removal. -class DiscoveryMethod { -public: - virtual ~DiscoveryMethod() = default; - virtual absl::optional derivePeerInfo(const StreamInfo::StreamInfo&, Http::HeaderMap&, - Context&) const PURE; - virtual void remove(Http::HeaderMap&) const {} -}; - -using DiscoveryMethodPtr = std::unique_ptr; - -class MXMethod : public DiscoveryMethod { -public: - MXMethod(bool downstream, const absl::flat_hash_set additional_labels, - Server::Configuration::ServerFactoryContext& factory_context); - absl::optional derivePeerInfo(const StreamInfo::StreamInfo&, Http::HeaderMap&, - Context&) const override; - void remove(Http::HeaderMap&) const override; - -private: - absl::optional lookup(absl::string_view id, absl::string_view value) const; - const bool downstream_; - struct MXCache : public ThreadLocal::ThreadLocalObject { - absl::flat_hash_map cache_; - }; - mutable ThreadLocal::TypedSlot tls_; - const absl::flat_hash_set additional_labels_; - const int64_t max_peer_cache_size_{500}; -}; - -// Base class for the propagation methods. -class PropagationMethod { -public: - virtual ~PropagationMethod() = default; - virtual void inject(const StreamInfo::StreamInfo&, Http::HeaderMap&, Context&) const PURE; -}; - -using PropagationMethodPtr = std::unique_ptr; - -class MXPropagationMethod : public PropagationMethod { -public: - MXPropagationMethod(bool downstream, Server::Configuration::ServerFactoryContext& factory_context, - const absl::flat_hash_set& additional_labels, - const io::istio::http::peer_metadata::Config_IstioHeaders&); - void inject(const StreamInfo::StreamInfo&, Http::HeaderMap&, Context&) const override; - -private: - const bool downstream_; - std::string computeValue(const absl::flat_hash_set&, - Server::Configuration::ServerFactoryContext&) const; - const std::string id_; - const std::string value_; - const bool skip_external_clusters_; - bool skipMXHeaders(const bool, const StreamInfo::StreamInfo&) const; -}; - -class FilterConfig : public Logger::Loggable { -public: - FilterConfig(const io::istio::http::peer_metadata::Config&, - Server::Configuration::FactoryContext&); - void discoverDownstream(StreamInfo::StreamInfo&, Http::RequestHeaderMap&, Context&) const; - void discoverUpstream(StreamInfo::StreamInfo&, Http::ResponseHeaderMap&, Context&) const; - void injectDownstream(const StreamInfo::StreamInfo&, Http::ResponseHeaderMap&, Context&) const; - void injectUpstream(const StreamInfo::StreamInfo&, Http::RequestHeaderMap&, Context&) const; - - static const CelStatePrototype& peerInfoPrototype() { - static const CelStatePrototype* const prototype = new CelStatePrototype( - true, CelStateType::Protobuf, "type.googleapis.com/google.protobuf.Struct", - StreamInfo::FilterState::LifeSpan::FilterChain); - return *prototype; - } - -private: - std::vector buildDiscoveryMethods( - const Protobuf::RepeatedPtrField&, - const absl::flat_hash_set& additional_labels, bool downstream, - Server::Configuration::FactoryContext&) const; - std::vector buildPropagationMethods( - const Protobuf::RepeatedPtrField&, - const absl::flat_hash_set& additional_labels, bool downstream, - Server::Configuration::FactoryContext&) const; - absl::flat_hash_set - buildAdditionalLabels(const Protobuf::RepeatedPtrField&) const; - StreamInfo::StreamSharingMayImpactPooling sharedWithUpstream() const { - return shared_with_upstream_ - ? StreamInfo::StreamSharingMayImpactPooling::SharedWithUpstreamConnectionOnce - : StreamInfo::StreamSharingMayImpactPooling::None; - } - void discover(StreamInfo::StreamInfo&, bool downstream, Http::HeaderMap&, Context&) const; - void setFilterState(StreamInfo::StreamInfo&, bool downstream, const PeerInfo& value) const; - const bool shared_with_upstream_; - const std::vector downstream_discovery_; - const std::vector upstream_discovery_; - const std::vector downstream_propagation_; - const std::vector upstream_propagation_; -}; - -using FilterConfigSharedPtr = std::shared_ptr; - -class Filter : public Http::PassThroughFilter { -public: - Filter(const FilterConfigSharedPtr& config) : config_(config) {} - Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap&, bool) override; - Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap&, bool) override; - -private: - FilterConfigSharedPtr config_; - Context ctx_; -}; - -class FilterConfigFactory : public Server::Configuration::NamedHttpFilterConfigFactory { -public: - std::string name() const override { return "envoy.filters.http.peer_metadata"; } - - ProtobufTypes::MessagePtr createEmptyConfigProto() override { - return std::make_unique(); - } - - absl::StatusOr - createFilterFactoryFromProto(const Protobuf::Message& proto_config, const std::string&, - Server::Configuration::FactoryContext&) override; -}; - -} // namespace PeerMetadata -} // namespace HttpFilters -} // namespace Extensions -} // namespace Envoy diff --git a/source/extensions/filters/http/peer_metadata/filter_test.cc b/source/extensions/filters/http/peer_metadata/filter_test.cc deleted file mode 100644 index 995da18b224..00000000000 --- a/source/extensions/filters/http/peer_metadata/filter_test.cc +++ /dev/null @@ -1,495 +0,0 @@ -// Copyright Istio Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "source/extensions/filters/http/peer_metadata/filter.h" - -#include "source/common/network/address_impl.h" -#include "test/common/stream_info/test_util.h" -#include "test/mocks/stream_info/mocks.h" -#include "test/mocks/server/factory_context.h" -#include "test/test_common/utility.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -using Istio::Common::WorkloadMetadataObject; -using testing::HasSubstr; -using testing::Invoke; -using testing::Return; -using testing::ReturnRef; - -namespace Envoy { -namespace Extensions { -namespace HttpFilters { -namespace PeerMetadata { -namespace { - -class MockSingletonManager : public Singleton::Manager { -public: - MockSingletonManager() {} - ~MockSingletonManager() override {} - MOCK_METHOD(Singleton::InstanceSharedPtr, get, - (const std::string& name, Singleton::SingletonFactoryCb cb, bool pin)); -}; -class MockWorkloadMetadataProvider - : public Extensions::Common::WorkloadDiscovery::WorkloadMetadataProvider, - public Singleton::Instance { -public: - MockWorkloadMetadataProvider() {} - ~MockWorkloadMetadataProvider() override {} - MOCK_METHOD(std::optional, GetMetadata, - (const Network::Address::InstanceConstSharedPtr& address)); -}; - -class PeerMetadataTest : public testing::Test { -protected: - PeerMetadataTest() { - ON_CALL(context_.server_factory_context_, singletonManager()) - .WillByDefault(ReturnRef(singleton_manager_)); - metadata_provider_ = std::make_shared>(); - ON_CALL(singleton_manager_, get(HasSubstr("workload_metadata_provider"), _, _)) - .WillByDefault(Return(metadata_provider_)); - } - void initialize(const std::string& yaml_config) { - TestUtility::loadFromYaml(yaml_config, config_); - FilterConfigFactory factory; - Http::FilterFactoryCb cb = factory.createFilterFactoryFromProto(config_, "", context_).value(); - Http::MockFilterChainFactoryCallbacks filter_callback; - ON_CALL(filter_callback, addStreamFilter(_)).WillByDefault(testing::SaveArg<0>(&filter_)); - EXPECT_CALL(filter_callback, addStreamFilter(_)); - cb(filter_callback); - ON_CALL(decoder_callbacks_, streamInfo()).WillByDefault(testing::ReturnRef(stream_info_)); - filter_->setDecoderFilterCallbacks(decoder_callbacks_); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, true)); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, true)); - } - void checkNoPeer(bool downstream) { - EXPECT_FALSE(stream_info_.filterState()->hasDataWithName( - downstream ? Istio::Common::DownstreamPeer : Istio::Common::UpstreamPeer)); - } - void checkPeerNamespace(bool downstream, const std::string& expected) { - const auto* cel_state = - stream_info_.filterState() - ->getDataReadOnly( - downstream ? Istio::Common::DownstreamPeer : Istio::Common::UpstreamPeer); - Protobuf::Struct obj; - ASSERT_TRUE(obj.ParseFromString(cel_state->value().data())); - EXPECT_EQ(expected, extractString(obj, "namespace")); - } - - absl::string_view extractString(const Protobuf::Struct& metadata, absl::string_view key) { - const auto& it = metadata.fields().find(key); - if (it == metadata.fields().end()) { - return {}; - } - return it->second.string_value(); - } - - void checkShared(bool expected) { - EXPECT_EQ(expected, - stream_info_.filterState()->objectsSharedWithUpstreamConnection()->size() > 0); - } - NiceMock context_; - NiceMock singleton_manager_; - std::shared_ptr> metadata_provider_; - NiceMock stream_info_; - NiceMock decoder_callbacks_; - Http::TestRequestHeaderMapImpl request_headers_; - Http::TestResponseHeaderMapImpl response_headers_; - io::istio::http::peer_metadata::Config config_; - Http::StreamFilterSharedPtr filter_; -}; - -TEST_F(PeerMetadataTest, None) { - initialize("{}"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkNoPeer(false); -} - -TEST_F(PeerMetadataTest, DownstreamXDSNone) { - EXPECT_CALL(*metadata_provider_, GetMetadata(_)).WillRepeatedly(Return(std::nullopt)); - initialize(R"EOF( - downstream_discovery: - - workload_discovery: {} - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkNoPeer(false); -} - -TEST_F(PeerMetadataTest, DownstreamXDS) { - const WorkloadMetadataObject pod("pod-foo-1234", "my-cluster", "default", "foo", "foo-service", - "v1alpha3", "", "", Istio::Common::WorkloadType::Pod, ""); - EXPECT_CALL(*metadata_provider_, GetMetadata(_)) - .WillRepeatedly(Invoke([&](const Network::Address::InstanceConstSharedPtr& address) - -> std::optional { - if (absl::StartsWith(address->asStringView(), "127.0.0.1")) { - return {pod}; - } - return {}; - })); - initialize(R"EOF( - downstream_discovery: - - workload_discovery: {} - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkPeerNamespace(true, "default"); - checkNoPeer(false); - checkShared(false); -} - -TEST_F(PeerMetadataTest, UpstreamXDS) { - const WorkloadMetadataObject pod("pod-foo-1234", "my-cluster", "foo", "foo", "foo-service", - "v1alpha3", "", "", Istio::Common::WorkloadType::Pod, ""); - EXPECT_CALL(*metadata_provider_, GetMetadata(_)) - .WillRepeatedly(Invoke([&](const Network::Address::InstanceConstSharedPtr& address) - -> std::optional { - if (absl::StartsWith(address->asStringView(), "10.0.0.1")) { - return {pod}; - } - return {}; - })); - initialize(R"EOF( - upstream_discovery: - - workload_discovery: {} - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkPeerNamespace(false, "foo"); -} - -TEST_F(PeerMetadataTest, UpstreamXDSInternal) { - Network::Address::InstanceConstSharedPtr upstream_address = - std::make_shared("internal_address", "endpoint_id"); - std::shared_ptr> upstream_host( - new NiceMock()); - EXPECT_CALL(*upstream_host, address()).WillRepeatedly(Return(upstream_address)); - stream_info_.upstreamInfo()->setUpstreamHost(upstream_host); - auto host_metadata = std::make_shared(); - ON_CALL(*upstream_host, metadata()).WillByDefault(testing::Return(host_metadata)); - TestUtility::loadFromYaml(R"EOF( - filter_metadata: - envoy.filters.listener.original_dst: - local: 127.0.0.100:80 - )EOF", - *host_metadata); - - const WorkloadMetadataObject pod("pod-foo-1234", "my-cluster", "foo", "foo", "foo-service", - "v1alpha3", "", "", Istio::Common::WorkloadType::Pod, ""); - EXPECT_CALL(*metadata_provider_, GetMetadata(_)) - .WillRepeatedly(Invoke([&](const Network::Address::InstanceConstSharedPtr& address) - -> std::optional { - if (absl::StartsWith(address->asStringView(), "127.0.0.100")) { - return {pod}; - } - return {}; - })); - initialize(R"EOF( - upstream_discovery: - - workload_discovery: {} - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkPeerNamespace(false, "foo"); -} - -TEST_F(PeerMetadataTest, DownstreamMXEmpty) { - initialize(R"EOF( - downstream_discovery: - - istio_headers: {} - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkNoPeer(false); -} - -constexpr absl::string_view SampleIstioHeader = - "ChIKBWlzdGlvEgkaB3NpZGVjYXIKDgoIU1RTX1BPUlQSAhoAChEKB01FU0hfSUQSBhoEbWVzaAocChZTVEFDS0RSSVZFUl" - "9UT0tFTl9GSUxFEgIaAAowCihTVEFDS0RSSVZFUl9MT0dHSU5HX0VYUE9SVF9JTlRFUlZBTF9TRUNTEgQaAjIwCjYKDElO" - "U1RBTkNFX0lQUxImGiQxMC41Mi4wLjM0LGZlODA6OmEwNzU6MTFmZjpmZTVlOmYxY2QKFAoDYXBwEg0aC3Byb2R1Y3RwYW" - "dlCisKG1NFQ1VSRV9TVEFDS0RSSVZFUl9FTkRQT0lOVBIMGgpsb2NhbGhvc3Q6Cl0KGmt1YmVybmV0ZXMuaW8vbGltaXQt" - "cmFuZ2VyEj8aPUxpbWl0UmFuZ2VyIHBsdWdpbiBzZXQ6IGNwdSByZXF1ZXN0IGZvciBjb250YWluZXIgcHJvZHVjdHBhZ2" - "UKIQoNV09SS0xPQURfTkFNRRIQGg5wcm9kdWN0cGFnZS12MQofChFJTlRFUkNFUFRJT05fTU9ERRIKGghSRURJUkVDVAoe" - "CgpDTFVTVEVSX0lEEhAaDmNsaWVudC1jbHVzdGVyCkkKD0lTVElPX1BST1hZX1NIQRI2GjRpc3Rpby1wcm94eTo0N2U0NT" - "U5YjhlNGYwZDUxNmMwZDE3YjIzM2QxMjdhM2RlYjNkN2NlClIKBU9XTkVSEkkaR2t1YmVybmV0ZXM6Ly9hcGlzL2FwcHMv" - "djEvbmFtZXNwYWNlcy9kZWZhdWx0L2RlcGxveW1lbnRzL3Byb2R1Y3RwYWdlLXYxCsEBCgZMQUJFTFMStgEqswEKFAoDYX" - "BwEg0aC3Byb2R1Y3RwYWdlCiEKEXBvZC10ZW1wbGF0ZS1oYXNoEgwaCjg0OTc1YmM3NzgKMwofc2VydmljZS5pc3Rpby5p" - "by9jYW5vbmljYWwtbmFtZRIQGg5wcm9kdWN0cGFnZS12MQoyCiNzZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1yZXZpc2" - "lvbhILGgl2ZXJzaW9uLTEKDwoHdmVyc2lvbhIEGgJ2MQopCgROQU1FEiEaH3Byb2R1Y3RwYWdlLXYxLTg0OTc1YmM3Nzgt" - "cHh6MncKLQoIUE9EX05BTUUSIRofcHJvZHVjdHBhZ2UtdjEtODQ5NzViYzc3OC1weHoydwoaCg1JU1RJT19WRVJTSU9OEg" - "kaBzEuNS1kZXYKHwoVSU5DTFVERV9JTkJPVU5EX1BPUlRTEgYaBDkwODAKmwEKEVBMQVRGT1JNX01FVEFEQVRBEoUBKoIB" - "CiYKFGdjcF9na2VfY2x1c3Rlcl9uYW1lEg4aDHRlc3QtY2x1c3RlcgocCgxnY3BfbG9jYXRpb24SDBoKdXMtZWFzdDQtYg" - "odCgtnY3BfcHJvamVjdBIOGgx0ZXN0LXByb2plY3QKGwoSZ2NwX3Byb2plY3RfbnVtYmVyEgUaAzEyMwopCg9TRVJWSUNF" - "X0FDQ09VTlQSFhoUYm9va2luZm8tcHJvZHVjdHBhZ2UKHQoQQ09ORklHX05BTUVTUEFDRRIJGgdkZWZhdWx0Cg8KB3Zlcn" - "Npb24SBBoCdjEKHgoYU1RBQ0tEUklWRVJfUk9PVF9DQV9GSUxFEgIaAAohChFwb2QtdGVtcGxhdGUtaGFzaBIMGgo4NDk3" - "NWJjNzc4Ch8KDkFQUF9DT05UQUlORVJTEg0aC3Rlc3QsYm9uemFpChYKCU5BTUVTUEFDRRIJGgdkZWZhdWx0CjMKK1NUQU" - "NLRFJJVkVSX01PTklUT1JJTkdfRVhQT1JUX0lOVEVSVkFMX1NFQ1MSBBoCMjA"; - -TEST_F(PeerMetadataTest, DownstreamFallbackFirst) { - request_headers_.setReference(Headers::get().ExchangeMetadataHeaderId, "test-pod"); - request_headers_.setReference(Headers::get().ExchangeMetadataHeader, SampleIstioHeader); - EXPECT_CALL(*metadata_provider_, GetMetadata(_)).Times(0); - initialize(R"EOF( - downstream_discovery: - - istio_headers: {} - - workload_discovery: {} - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkPeerNamespace(true, "default"); - checkNoPeer(false); -} - -TEST_F(PeerMetadataTest, DownstreamFallbackSecond) { - const WorkloadMetadataObject pod("pod-foo-1234", "my-cluster", "default", "foo", "foo-service", - "v1alpha3", "", "", Istio::Common::WorkloadType::Pod, ""); - EXPECT_CALL(*metadata_provider_, GetMetadata(_)) - .WillRepeatedly(Invoke([&](const Network::Address::InstanceConstSharedPtr& address) - -> std::optional { - if (absl::StartsWith(address->asStringView(), "127.0.0.1")) { // remote address - return {pod}; - } - return {}; - })); - initialize(R"EOF( - downstream_discovery: - - istio_headers: {} - - workload_discovery: {} - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkPeerNamespace(true, "default"); - checkNoPeer(false); -} - -TEST(MXMethod, Cache) { - NiceMock context; - absl::flat_hash_set additional_labels; - MXMethod method(true, additional_labels, context); - NiceMock stream_info; - Http::TestRequestHeaderMapImpl request_headers; - const int32_t max = 1000; - for (int32_t run = 0; run < 3; run++) { - for (int32_t i = 0; i < max; i++) { - std::string id = absl::StrCat("test-", i); - request_headers.setReference(Headers::get().ExchangeMetadataHeaderId, id); - request_headers.setReference(Headers::get().ExchangeMetadataHeader, SampleIstioHeader); - Context ctx; - const auto result = method.derivePeerInfo(stream_info, request_headers, ctx); - EXPECT_TRUE(result.has_value()); - } - } -} - -TEST_F(PeerMetadataTest, DownstreamMX) { - request_headers_.setReference(Headers::get().ExchangeMetadataHeaderId, "test-pod"); - request_headers_.setReference(Headers::get().ExchangeMetadataHeader, SampleIstioHeader); - initialize(R"EOF( - downstream_discovery: - - istio_headers: {} - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkPeerNamespace(true, "default"); - checkNoPeer(false); - checkShared(false); -} - -TEST_F(PeerMetadataTest, UpstreamMX) { - response_headers_.setReference(Headers::get().ExchangeMetadataHeaderId, "test-pod"); - response_headers_.setReference(Headers::get().ExchangeMetadataHeader, SampleIstioHeader); - initialize(R"EOF( - upstream_discovery: - - istio_headers: {} - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkPeerNamespace(false, "default"); -} - -TEST_F(PeerMetadataTest, UpstreamFallbackFirst) { - EXPECT_CALL(*metadata_provider_, GetMetadata(_)).Times(0); - response_headers_.setReference(Headers::get().ExchangeMetadataHeaderId, "test-pod"); - response_headers_.setReference(Headers::get().ExchangeMetadataHeader, SampleIstioHeader); - initialize(R"EOF( - upstream_discovery: - - istio_headers: {} - - workload_discovery: {} - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkPeerNamespace(false, "default"); -} - -TEST_F(PeerMetadataTest, UpstreamFallbackSecond) { - const WorkloadMetadataObject pod("pod-foo-1234", "my-cluster", "foo", "foo", "foo-service", - "v1alpha3", "", "", Istio::Common::WorkloadType::Pod, ""); - EXPECT_CALL(*metadata_provider_, GetMetadata(_)) - .WillRepeatedly(Invoke([&](const Network::Address::InstanceConstSharedPtr& address) - -> std::optional { - if (absl::StartsWith(address->asStringView(), "10.0.0.1")) { // upstream host address - return {pod}; - } - return {}; - })); - initialize(R"EOF( - upstream_discovery: - - istio_headers: {} - - workload_discovery: {} - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkPeerNamespace(false, "foo"); -} - -TEST_F(PeerMetadataTest, UpstreamFallbackFirstXDS) { - const WorkloadMetadataObject pod("pod-foo-1234", "my-cluster", "foo", "foo", "foo-service", - "v1alpha3", "", "", Istio::Common::WorkloadType::Pod, ""); - EXPECT_CALL(*metadata_provider_, GetMetadata(_)) - .WillRepeatedly(Invoke([&](const Network::Address::InstanceConstSharedPtr& address) - -> std::optional { - if (absl::StartsWith(address->asStringView(), "10.0.0.1")) { // upstream host address - return {pod}; - } - return {}; - })); - response_headers_.setReference(Headers::get().ExchangeMetadataHeaderId, "test-pod"); - response_headers_.setReference(Headers::get().ExchangeMetadataHeader, SampleIstioHeader); - initialize(R"EOF( - upstream_discovery: - - workload_discovery: {} - - istio_headers: {} - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkPeerNamespace(false, "foo"); -} - -TEST_F(PeerMetadataTest, DownstreamMXPropagation) { - initialize(R"EOF( - downstream_propagation: - - istio_headers: {} - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkNoPeer(false); -} - -TEST_F(PeerMetadataTest, DownstreamMXPropagationWithAdditionalLabels) { - initialize(R"EOF( - downstream_propagation: - - istio_headers: {} - additional_labels: - - foo - - bar - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkNoPeer(false); -} - -TEST_F(PeerMetadataTest, DownstreamMXDiscoveryPropagation) { - request_headers_.setReference(Headers::get().ExchangeMetadataHeaderId, "test-pod"); - request_headers_.setReference(Headers::get().ExchangeMetadataHeader, SampleIstioHeader); - initialize(R"EOF( - downstream_discovery: - - istio_headers: {} - downstream_propagation: - - istio_headers: {} - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(2, response_headers_.size()); - checkPeerNamespace(true, "default"); - checkNoPeer(false); -} - -TEST_F(PeerMetadataTest, UpstreamMXPropagation) { - initialize(R"EOF( - upstream_propagation: - - istio_headers: - skip_external_clusters: false - )EOF"); - EXPECT_EQ(2, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkNoPeer(false); -} - -TEST_F(PeerMetadataTest, UpstreamMXPropagationSkipNoMatch) { - initialize(R"EOF( - upstream_propagation: - - istio_headers: - skip_external_clusters: true - )EOF"); - EXPECT_EQ(2, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkNoPeer(false); -} - -TEST_F(PeerMetadataTest, UpstreamMXPropagationSkip) { - std::shared_ptr cluster_info_{ - std::make_shared>()}; - auto metadata = TestUtility::parseYaml(R"EOF( - filter_metadata: - istio: - external: true - )EOF"); - ON_CALL(stream_info_, upstreamClusterInfo()).WillByDefault(testing::Return(cluster_info_)); - ON_CALL(*cluster_info_, metadata()).WillByDefault(ReturnRef(metadata)); - initialize(R"EOF( - upstream_propagation: - - istio_headers: - skip_external_clusters: true - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkNoPeer(false); -} - -TEST_F(PeerMetadataTest, UpstreamMXPropagationSkipPassthrough) { - std::shared_ptr cluster_info_{ - std::make_shared>()}; - cluster_info_->name_ = "PassthroughCluster"; - ON_CALL(stream_info_, upstreamClusterInfo()).WillByDefault(testing::Return(cluster_info_)); - initialize(R"EOF( - upstream_propagation: - - istio_headers: - skip_external_clusters: true - )EOF"); - EXPECT_EQ(0, request_headers_.size()); - EXPECT_EQ(0, response_headers_.size()); - checkNoPeer(true); - checkNoPeer(false); -} - -} // namespace -} // namespace PeerMetadata -} // namespace HttpFilters -} // namespace Extensions -} // namespace Envoy diff --git a/source/extensions/filters/network/metadata_exchange/BUILD b/source/extensions/filters/network/metadata_exchange/BUILD deleted file mode 100644 index 28a6bc7d883..00000000000 --- a/source/extensions/filters/network/metadata_exchange/BUILD +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright 2019 Istio Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ -# - -# Metadata Exchange filter - -load( - "@envoy//bazel:envoy_build_system.bzl", - "envoy_cc_library", - "envoy_cc_test", -) - -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -envoy_cc_library( - name = "metadata_exchange", - srcs = [ - "metadata_exchange.cc", - "metadata_exchange_initial_header.cc", - ], - hdrs = [ - "metadata_exchange.h", - "metadata_exchange_initial_header.h", - ], - repository = "@envoy", - deps = [ - "//extensions/common:metadata_object_lib", - "//source/extensions/common/workload_discovery:api_lib", - "//source/extensions/filters/network/metadata_exchange/config:metadata_exchange_cc_proto", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/strings", - "@envoy//envoy/local_info:local_info_interface", - "@envoy//envoy/network:connection_interface", - "@envoy//envoy/network:filter_interface", - "@envoy//envoy/runtime:runtime_interface", - "@envoy//envoy/stats:stats_macros", - "@envoy//envoy/stream_info:filter_state_interface", - "@envoy//source/common/http:utility_lib", - "@envoy//source/common/network:filter_state_dst_address_lib", - "@envoy//source/common/network:utility_lib", - "@envoy//source/common/protobuf", - "@envoy//source/common/protobuf:utility_lib", - "@envoy//source/common/stream_info:bool_accessor_lib", - "@envoy//source/extensions/filters/common/expr:cel_state_lib", - ], -) - -envoy_cc_library( - name = "config_lib", - srcs = ["config.cc"], - hdrs = ["config.h"], - repository = "@envoy", - deps = [ - ":metadata_exchange", - "//source/extensions/filters/network/metadata_exchange/config:metadata_exchange_cc_proto", - "@envoy//envoy/registry", - "@envoy//envoy/server:filter_config_interface", - ], -) - -envoy_cc_test( - name = "metadataexchange_test", - srcs = [ - "metadata_exchange_test.cc", - ], - repository = "@envoy", - deps = [ - ":config_lib", - ":metadata_exchange", - "@envoy//source/common/protobuf", - "@envoy//test/mocks/local_info:local_info_mocks", - "@envoy//test/mocks/network:network_mocks", - "@envoy//test/mocks/protobuf:protobuf_mocks", - "@envoy//test/mocks/server:server_factory_context_mocks", - ], -) diff --git a/source/extensions/filters/network/metadata_exchange/config.cc b/source/extensions/filters/network/metadata_exchange/config.cc deleted file mode 100644 index 215770c680d..00000000000 --- a/source/extensions/filters/network/metadata_exchange/config.cc +++ /dev/null @@ -1,104 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "source/extensions/filters/network/metadata_exchange/config.h" - -#include "envoy/network/connection.h" -#include "envoy/registry/registry.h" -#include "source/extensions/filters/network/metadata_exchange/metadata_exchange.h" - -namespace Envoy { -namespace Tcp { -namespace MetadataExchange { -namespace { - -static constexpr char StatPrefix[] = "metadata_exchange."; - -Network::FilterFactoryCb createFilterFactoryHelper( - const envoy::tcp::metadataexchange::config::MetadataExchange& proto_config, - Server::Configuration::ServerFactoryContext& context, FilterDirection filter_direction) { - ASSERT(!proto_config.protocol().empty()); - - absl::flat_hash_set additional_labels; - if (!proto_config.additional_labels().empty()) { - for (const auto& label : proto_config.additional_labels()) { - additional_labels.emplace(label); - } - } - - MetadataExchangeConfigSharedPtr filter_config(std::make_shared( - StatPrefix, proto_config.protocol(), filter_direction, proto_config.enable_discovery(), - additional_labels, context, context.scope())); - return [filter_config, &context](Network::FilterManager& filter_manager) -> void { - filter_manager.addFilter( - std::make_shared(filter_config, context.localInfo())); - }; -} -} // namespace - -absl::StatusOr -MetadataExchangeConfigFactory::createFilterFactoryFromProto( - const Protobuf::Message& config, Server::Configuration::FactoryContext& context) { - return createFilterFactory( - dynamic_cast(config), context); -} - -ProtobufTypes::MessagePtr MetadataExchangeConfigFactory::createEmptyConfigProto() { - return std::make_unique(); -} - -Network::FilterFactoryCb MetadataExchangeConfigFactory::createFilterFactory( - const envoy::tcp::metadataexchange::config::MetadataExchange& proto_config, - Server::Configuration::FactoryContext& context) { - return createFilterFactoryHelper(proto_config, context.serverFactoryContext(), - FilterDirection::Downstream); -} - -Network::FilterFactoryCb MetadataExchangeUpstreamConfigFactory::createFilterFactoryFromProto( - const Protobuf::Message& config, Server::Configuration::UpstreamFactoryContext& context) { - return createFilterFactory( - dynamic_cast(config), context); -} - -ProtobufTypes::MessagePtr MetadataExchangeUpstreamConfigFactory::createEmptyConfigProto() { - return std::make_unique(); -} - -Network::FilterFactoryCb MetadataExchangeUpstreamConfigFactory::createFilterFactory( - const envoy::tcp::metadataexchange::config::MetadataExchange& proto_config, - Server::Configuration::UpstreamFactoryContext& context) { - return createFilterFactoryHelper(proto_config, context.serverFactoryContext(), - FilterDirection::Upstream); -} - -/** - * Static registration for the MetadataExchange Downstream filter. @see - * RegisterFactory. - */ -static Registry::RegisterFactory - registered_; - -/** - * Static registration for the MetadataExchange Upstream filter. @see - * RegisterFactory. - */ -static Registry::RegisterFactory - registered_upstream_; - -} // namespace MetadataExchange -} // namespace Tcp -} // namespace Envoy diff --git a/source/extensions/filters/network/metadata_exchange/config.h b/source/extensions/filters/network/metadata_exchange/config.h deleted file mode 100644 index d4a215569f2..00000000000 --- a/source/extensions/filters/network/metadata_exchange/config.h +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "envoy/server/filter_config.h" -#include "source/extensions/filters/network/metadata_exchange/config/metadata_exchange.pb.h" - -namespace Envoy { -namespace Tcp { -namespace MetadataExchange { - -/** - * Config registration for the MetadataExchange filter. @see - * NamedNetworkFilterConfigFactory. - */ -class MetadataExchangeConfigFactory - : public Server::Configuration::NamedNetworkFilterConfigFactory { -public: - absl::StatusOr - createFilterFactoryFromProto(const Protobuf::Message&, - Server::Configuration::FactoryContext&) override; - - ProtobufTypes::MessagePtr createEmptyConfigProto() override; - - std::string name() const override { return "envoy.filters.network.metadata_exchange"; } - -private: - Network::FilterFactoryCb - createFilterFactory(const envoy::tcp::metadataexchange::config::MetadataExchange& proto_config, - Server::Configuration::FactoryContext& context); -}; - -/** - * Config registration for the MetadataExchange Upstream filter. @see - * NamedUpstreamNetworkFilterConfigFactory. - */ -class MetadataExchangeUpstreamConfigFactory - : public Server::Configuration::NamedUpstreamNetworkFilterConfigFactory { -public: - Network::FilterFactoryCb - createFilterFactoryFromProto(const Protobuf::Message&, - Server::Configuration::UpstreamFactoryContext&) override; - - ProtobufTypes::MessagePtr createEmptyConfigProto() override; - - std::string name() const override { return "envoy.filters.network.upstream.metadata_exchange"; } - -private: - Network::FilterFactoryCb - createFilterFactory(const envoy::tcp::metadataexchange::config::MetadataExchange& proto_config, - Server::Configuration::UpstreamFactoryContext& context); -}; - -} // namespace MetadataExchange -} // namespace Tcp -} // namespace Envoy diff --git a/source/extensions/filters/network/metadata_exchange/config/BUILD b/source/extensions/filters/network/metadata_exchange/config/BUILD deleted file mode 100644 index 160ee90e7fe..00000000000 --- a/source/extensions/filters/network/metadata_exchange/config/BUILD +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2019 Istio Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -################################################################################ -# -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -proto_library( - name = "metadata_exchange_proto", - srcs = ["metadata_exchange.proto"], -) - -cc_proto_library( - name = "metadata_exchange_cc_proto", - deps = ["metadata_exchange_proto"], -) diff --git a/source/extensions/filters/network/metadata_exchange/config/metadata_exchange.proto b/source/extensions/filters/network/metadata_exchange/config/metadata_exchange.proto deleted file mode 100644 index 332b0c507b6..00000000000 --- a/source/extensions/filters/network/metadata_exchange/config/metadata_exchange.proto +++ /dev/null @@ -1,38 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto3"; - -package envoy.tcp.metadataexchange.config; - -option java_outer_classname = "MetadataExchangeProto"; -option java_multiple_files = true; -option java_package = "io.envoyproxy.envoy.tcp.metadataexchange.config"; -option go_package = "MetadataExchange"; - -// [#protodoc-title: MetadataExchange protocol match and data transfer] -// MetadataExchange protocol match and data transfer -message MetadataExchange { - // Protocol that Alpn should support on the server. - // [#comment:TODO(GargNupur): Make it a list.] - string protocol = 1; - - // If true, will attempt to use WDS in case the prefix peer metadata is not available. - bool enable_discovery = 2; - - // Additional labels to be added to the peer metadata to help your understand the traffic. - // e.g. `role`, `location` etc. - repeated string additional_labels = 3; -} diff --git a/source/extensions/filters/network/metadata_exchange/metadata_exchange.cc b/source/extensions/filters/network/metadata_exchange/metadata_exchange.cc deleted file mode 100644 index 997d32e182c..00000000000 --- a/source/extensions/filters/network/metadata_exchange/metadata_exchange.cc +++ /dev/null @@ -1,367 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "source/extensions/filters/network/metadata_exchange/metadata_exchange.h" - -#include -#include - -#include "absl/base/internal/endian.h" -#include "absl/strings/str_split.h" -#include "absl/strings/string_view.h" -#include "envoy/network/connection.h" -#include "envoy/stats/scope.h" -#include "source/common/buffer/buffer_impl.h" -#include "source/common/protobuf/utility.h" -#include "source/common/network/utility.h" -#include "source/common/network/filter_state_dst_address.h" -#include "source/extensions/filters/network/metadata_exchange/metadata_exchange_initial_header.h" -#include "source/common/stream_info/bool_accessor_impl.h" - -namespace Envoy { -namespace Tcp { -namespace MetadataExchange { - -using ::Envoy::Extensions::Filters::Common::Expr::CelState; - -namespace { - -// Sentinel key in the filter state, indicating that the peer metadata is -// decidedly absent. This is different from a missing peer metadata ID key -// which could indicate that the metadata is not received yet. -const std::string kMetadataNotFoundValue = "envoy.wasm.metadata_exchange.peer_unknown"; - -std::unique_ptr constructProxyHeaderData(const Protobuf::Any& proxy_data) { - MetadataExchangeInitialHeader initial_header; - std::string proxy_data_str = proxy_data.SerializeAsString(); - // Converting from host to network byte order so that most significant byte is - // placed first. - initial_header.magic = absl::ghtonl(MetadataExchangeInitialHeader::magic_number); - initial_header.data_size = absl::ghtonl(proxy_data_str.length()); - - Buffer::OwnedImpl initial_header_buffer{absl::string_view( - reinterpret_cast(&initial_header), sizeof(MetadataExchangeInitialHeader))}; - auto proxy_data_buffer = std::make_unique(proxy_data_str); - proxy_data_buffer->prepend(initial_header_buffer); - return proxy_data_buffer; -} - -} // namespace - -MetadataExchangeConfig::MetadataExchangeConfig( - const std::string& stat_prefix, const std::string& protocol, - const FilterDirection filter_direction, bool enable_discovery, - const absl::flat_hash_set additional_labels, - Server::Configuration::ServerFactoryContext& factory_context, Stats::Scope& scope) - : scope_(scope), stat_prefix_(stat_prefix), protocol_(protocol), - filter_direction_(filter_direction), stats_(generateStats(stat_prefix, scope)), - additional_labels_(additional_labels) { - if (enable_discovery) { - metadata_provider_ = Extensions::Common::WorkloadDiscovery::GetProvider(factory_context); - } -} - -Network::FilterStatus MetadataExchangeFilter::onData(Buffer::Instance& data, bool end_stream) { - switch (conn_state_) { - case Invalid: - FALLTHRU; - case Done: - // No work needed if connection state is Done or Invalid. - return Network::FilterStatus::Continue; - case ConnProtocolNotRead: { - // If Alpn protocol is not the expected one, then return. - // Else find and write node metadata. - if (read_callbacks_->connection().nextProtocol() != config_->protocol_) { - ENVOY_LOG(trace, "Alpn Protocol Not Found. Expected {}, Got {}", config_->protocol_, - read_callbacks_->connection().nextProtocol()); - setMetadataNotFoundFilterState(); - conn_state_ = Invalid; - config_->stats().alpn_protocol_not_found_.inc(); - return Network::FilterStatus::Continue; - } - conn_state_ = WriteMetadata; - config_->stats().alpn_protocol_found_.inc(); - FALLTHRU; - } - case WriteMetadata: { - // TODO(gargnupur): Try to move this just after alpn protocol is - // determined and first onData is called in Downstream filter. - // If downstream filter, write metadata. - // Otherwise, go ahead and try to read initial header and proxy data. - writeNodeMetadata(); - FALLTHRU; - } - case ReadingInitialHeader: - case NeedMoreDataInitialHeader: { - tryReadInitialProxyHeader(data); - if (conn_state_ == NeedMoreDataInitialHeader) { - if (end_stream) { - // Upstream has entered a half-closed state, and will be sending no more data. - // Since this plugin would expect additional headers, but none is forthcoming, - // do not block the tcp_proxy downstream of us from draining the buffer. - ENVOY_LOG(debug, "Upstream closed early, aborting istio-peer-exchange"); - conn_state_ = Invalid; - return Network::FilterStatus::Continue; - } - return Network::FilterStatus::StopIteration; - } - if (conn_state_ == Invalid) { - return Network::FilterStatus::Continue; - } - FALLTHRU; - } - case ReadingProxyHeader: - case NeedMoreDataProxyHeader: { - tryReadProxyData(data); - if (conn_state_ == NeedMoreDataProxyHeader) { - return Network::FilterStatus::StopIteration; - } - if (conn_state_ == Invalid) { - return Network::FilterStatus::Continue; - } - FALLTHRU; - } - default: - conn_state_ = Done; - return Network::FilterStatus::Continue; - } - - return Network::FilterStatus::Continue; -} - -Network::FilterStatus MetadataExchangeFilter::onNewConnection() { - return Network::FilterStatus::Continue; -} - -Network::FilterStatus MetadataExchangeFilter::onWrite(Buffer::Instance&, bool) { - switch (conn_state_) { - case Invalid: - case Done: - // No work needed if connection state is Done or Invalid. - return Network::FilterStatus::Continue; - case ConnProtocolNotRead: { - if (read_callbacks_->connection().nextProtocol() != config_->protocol_) { - ENVOY_LOG(trace, "Alpn Protocol Not Found. Expected {}, Got {}", config_->protocol_, - read_callbacks_->connection().nextProtocol()); - setMetadataNotFoundFilterState(); - conn_state_ = Invalid; - config_->stats().alpn_protocol_not_found_.inc(); - return Network::FilterStatus::Continue; - } else { - conn_state_ = WriteMetadata; - config_->stats().alpn_protocol_found_.inc(); - } - FALLTHRU; - } - case WriteMetadata: { - // TODO(gargnupur): Try to move this just after alpn protocol is - // determined and first onWrite is called in Upstream filter. - writeNodeMetadata(); - FALLTHRU; - } - case ReadingInitialHeader: - case ReadingProxyHeader: - case NeedMoreDataInitialHeader: - case NeedMoreDataProxyHeader: - // These are to be handled in Reading Pipeline. - return Network::FilterStatus::Continue; - } - - return Network::FilterStatus::Continue; -} - -void MetadataExchangeFilter::writeNodeMetadata() { - if (conn_state_ != WriteMetadata) { - return; - } - ENVOY_LOG(trace, "Writing metadata to the connection."); - Protobuf::Struct data; - const auto obj = Istio::Common::convertStructToWorkloadMetadata(local_info_.node().metadata(), - config_->additional_labels_); - *(*data.mutable_fields())[ExchangeMetadataHeader].mutable_struct_value() = - Istio::Common::convertWorkloadMetadataToStruct(*obj); - std::string metadata_id = getMetadataId(); - if (!metadata_id.empty()) { - (*data.mutable_fields())[ExchangeMetadataHeaderId].set_string_value(metadata_id); - } - if (data.fields_size() > 0) { - Protobuf::Any metadata_any_value; - metadata_any_value.set_type_url(StructTypeUrl); - *metadata_any_value.mutable_value() = Istio::Common::serializeToStringDeterministic(data); - ; - std::unique_ptr buf = constructProxyHeaderData(metadata_any_value); - write_callbacks_->injectWriteDataToFilterChain(*buf, false); - config_->stats().metadata_added_.inc(); - } - - conn_state_ = ReadingInitialHeader; -} - -void MetadataExchangeFilter::tryReadInitialProxyHeader(Buffer::Instance& data) { - if (conn_state_ != ReadingInitialHeader && conn_state_ != NeedMoreDataInitialHeader) { - return; - } - const uint32_t initial_header_length = sizeof(MetadataExchangeInitialHeader); - if (data.length() < initial_header_length) { - config_->stats().initial_header_not_found_.inc(); - // Not enough data to read. Wait for it to come. - ENVOY_LOG(debug, "Alpn Protocol matched. Waiting to read more initial header."); - conn_state_ = NeedMoreDataInitialHeader; - return; - } - MetadataExchangeInitialHeader initial_header; - data.copyOut(0, initial_header_length, &initial_header); - if (absl::gntohl(initial_header.magic) != MetadataExchangeInitialHeader::magic_number) { - config_->stats().initial_header_not_found_.inc(); - setMetadataNotFoundFilterState(); - ENVOY_LOG(warn, "Incorrect istio-peer-exchange ALPN magic. Peer missing TCP " - "MetadataExchange filter."); - conn_state_ = Invalid; - return; - } - proxy_data_length_ = absl::gntohl(initial_header.data_size); - // Drain the initial header length bytes read. - data.drain(initial_header_length); - conn_state_ = ReadingProxyHeader; -} - -void MetadataExchangeFilter::tryReadProxyData(Buffer::Instance& data) { - if (conn_state_ != ReadingProxyHeader && conn_state_ != NeedMoreDataProxyHeader) { - return; - } - if (data.length() < proxy_data_length_) { - // Not enough data to read. Wait for it to come. - ENVOY_LOG(debug, "Alpn Protocol matched. Waiting to read more metadata."); - conn_state_ = NeedMoreDataProxyHeader; - return; - } - std::string proxy_data_buf = - std::string(static_cast(data.linearize(proxy_data_length_)), proxy_data_length_); - Protobuf::Any proxy_data; - if (!proxy_data.ParseFromString(proxy_data_buf)) { - config_->stats().header_not_found_.inc(); - setMetadataNotFoundFilterState(); - ENVOY_LOG(warn, "Alpn protocol matched. Magic matched. Metadata Not found."); - conn_state_ = Invalid; - return; - } - data.drain(proxy_data_length_); - - // Set Metadata - Protobuf::Struct value_struct = MessageUtil::anyConvert(proxy_data); - auto key_metadata_it = value_struct.fields().find(ExchangeMetadataHeader); - if (key_metadata_it != value_struct.fields().end()) { - updatePeer(*Istio::Common::convertStructToWorkloadMetadata( - key_metadata_it->second.struct_value(), config_->additional_labels_)); - } -} - -void MetadataExchangeFilter::updatePeer(const Istio::Common::WorkloadMetadataObject& value) { - updatePeer(value, config_->filter_direction_); -} - -void MetadataExchangeFilter::updatePeer(const Istio::Common::WorkloadMetadataObject& value, - FilterDirection direction) { - auto filter_state_key = direction == FilterDirection::Downstream ? Istio::Common::DownstreamPeer - : Istio::Common::UpstreamPeer; - auto pb = value.serializeAsProto(); - auto peer_info = std::make_shared(MetadataExchangeConfig::peerInfoPrototype()); - peer_info->setValue(absl::string_view(pb->SerializeAsString())); - - read_callbacks_->connection().streamInfo().filterState()->setData( - filter_state_key, std::move(peer_info), StreamInfo::FilterState::StateType::Mutable, - StreamInfo::FilterState::LifeSpan::Connection); -} - -std::string MetadataExchangeFilter::getMetadataId() { return local_info_.node().id(); } - -void MetadataExchangeFilter::setMetadataNotFoundFilterState() { - if (config_->metadata_provider_) { - Network::Address::InstanceConstSharedPtr upstream_peer; - const StreamInfo::StreamInfo& info = read_callbacks_->connection().streamInfo(); - if (info.upstreamInfo()) { - auto upstream_host = info.upstreamInfo().value().get().upstreamHost(); - if (upstream_host) { - const auto address = upstream_host->address(); - ENVOY_LOG(debug, "Trying to check upstream host info of host {}", address->asString()); - switch (address->type()) { - case Network::Address::Type::Ip: - upstream_peer = upstream_host->address(); - break; - case Network::Address::Type::EnvoyInternal: - if (upstream_host->metadata()) { - ENVOY_LOG(debug, "Trying to check filter metadata of host {}", - upstream_host->address()->asString()); - const auto& filter_metadata = upstream_host->metadata()->filter_metadata(); - const auto& it = filter_metadata.find("envoy.filters.listener.original_dst"); - if (it != filter_metadata.end()) { - const auto& destination_it = it->second.fields().find("local"); - if (destination_it != it->second.fields().end()) { - upstream_peer = Network::Utility::parseInternetAddressAndPortNoThrow( - destination_it->second.string_value(), /*v6only=*/false); - } - } - } - break; - default: - break; - } - } - } - // Get our metadata differently based on the direction of the filter - auto downstream_peer_address = [&]() -> Network::Address::InstanceConstSharedPtr { - if (upstream_peer) { - // Query upstream peer data and save it in metadata for stats - const auto metadata_object = config_->metadata_provider_->GetMetadata(upstream_peer); - if (metadata_object) { - ENVOY_LOG(debug, "Metadata found for upstream peer address {}", - upstream_peer->asString()); - updatePeer(metadata_object.value(), FilterDirection::Upstream); - } - } - - // Regardless, return the downstream address for downstream metadata - return read_callbacks_->connection().connectionInfoProvider().remoteAddress(); - }; - - auto upstream_peer_address = [&]() -> Network::Address::InstanceConstSharedPtr { - if (upstream_peer) { - return upstream_peer; - } - ENVOY_LOG(debug, "Upstream peer address is null. Fall back to localAddress"); - return read_callbacks_->connection().connectionInfoProvider().localAddress(); - }; - const Network::Address::InstanceConstSharedPtr peer_address = - config_->filter_direction_ == FilterDirection::Downstream ? downstream_peer_address() - : upstream_peer_address(); - ENVOY_LOG(debug, "Look up metadata based on peer address {}", peer_address->asString()); - const auto metadata_object = config_->metadata_provider_->GetMetadata(peer_address); - if (metadata_object) { - ENVOY_LOG(trace, "Metadata found for peer address {}", peer_address->asString()); - updatePeer(metadata_object.value()); - config_->stats().metadata_added_.inc(); - return; - } else { - ENVOY_LOG(debug, "Metadata not found for peer address {}", peer_address->asString()); - } - } - read_callbacks_->connection().streamInfo().filterState()->setData( - Istio::Common::NoPeer, std::make_shared(true), - StreamInfo::FilterState::StateType::Mutable, StreamInfo::FilterState::LifeSpan::Connection); -} - -} // namespace MetadataExchange -} // namespace Tcp -} // namespace Envoy diff --git a/source/extensions/filters/network/metadata_exchange/metadata_exchange.h b/source/extensions/filters/network/metadata_exchange/metadata_exchange.h deleted file mode 100644 index 678ccc6b702..00000000000 --- a/source/extensions/filters/network/metadata_exchange/metadata_exchange.h +++ /dev/null @@ -1,182 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "envoy/local_info/local_info.h" -#include "envoy/network/filter.h" -#include "envoy/runtime/runtime.h" -#include "envoy/stats/scope.h" -#include "envoy/stats/stats_macros.h" -#include "envoy/stream_info/filter_state.h" -#include "source/common/common/stl_helpers.h" -#include "source/common/protobuf/protobuf.h" -#include "source/extensions/filters/common/expr/cel_state.h" -#include "source/extensions/filters/network/metadata_exchange/config/metadata_exchange.pb.h" -#include "source/extensions/common/workload_discovery/api.h" - -#include "extensions/common/metadata_object.h" - -namespace Envoy { -namespace Tcp { -namespace MetadataExchange { - -using ::Envoy::Extensions::Filters::Common::Expr::CelStatePrototype; -using ::Envoy::Extensions::Filters::Common::Expr::CelStateType; - -/** - * All MetadataExchange filter stats. @see stats_macros.h - */ -#define ALL_METADATA_EXCHANGE_STATS(COUNTER) \ - COUNTER(alpn_protocol_not_found) \ - COUNTER(alpn_protocol_found) \ - COUNTER(initial_header_not_found) \ - COUNTER(header_not_found) \ - COUNTER(metadata_added) - -/** - * Struct definition for all MetadataExchange stats. @see stats_macros.h - */ -struct MetadataExchangeStats { - ALL_METADATA_EXCHANGE_STATS(GENERATE_COUNTER_STRUCT) -}; - -/** - * Direction of the flow of traffic in which this this MetadataExchange filter - * is placed. - */ -enum class FilterDirection { Downstream, Upstream }; - -/** - * Configuration for the MetadataExchange filter. - */ -class MetadataExchangeConfig { -public: - MetadataExchangeConfig(const std::string& stat_prefix, const std::string& protocol, - const FilterDirection filter_direction, bool enable_discovery, - const absl::flat_hash_set additional_labels, - Server::Configuration::ServerFactoryContext& factory_context, - Stats::Scope& scope); - - const MetadataExchangeStats& stats() { return stats_; } - - // Scope for the stats. - Stats::Scope& scope_; - // Stat prefix. - const std::string stat_prefix_; - // Expected Alpn Protocol. - const std::string protocol_; - // Direction of filter. - const FilterDirection filter_direction_; - // Set if WDS is enabled. - Extensions::Common::WorkloadDiscovery::WorkloadMetadataProviderSharedPtr metadata_provider_; - // Stats for MetadataExchange Filter. - MetadataExchangeStats stats_; - const absl::flat_hash_set additional_labels_; - - static const CelStatePrototype& peerInfoPrototype() { - static const CelStatePrototype* const prototype = new CelStatePrototype( - true, CelStateType::Protobuf, "type.googleapis.com/google.protobuf.Struct", - StreamInfo::FilterState::LifeSpan::Connection); - return *prototype; - } - -private: - MetadataExchangeStats generateStats(const std::string& prefix, Stats::Scope& scope) { - return MetadataExchangeStats{ALL_METADATA_EXCHANGE_STATS(POOL_COUNTER_PREFIX(scope, prefix))}; - } -}; - -using MetadataExchangeConfigSharedPtr = std::shared_ptr; - -/** - * A MetadataExchange filter instance. One per connection. - */ -class MetadataExchangeFilter : public Network::Filter, - protected Logger::Loggable { -public: - MetadataExchangeFilter(MetadataExchangeConfigSharedPtr config, - const LocalInfo::LocalInfo& local_info) - : config_(config), local_info_(local_info), conn_state_(ConnProtocolNotRead) {} - - // Network::ReadFilter - Network::FilterStatus onData(Buffer::Instance& data, bool end_stream) override; - Network::FilterStatus onNewConnection() override; - Network::FilterStatus onWrite(Buffer::Instance& data, bool end_stream) override; - void initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) override { - read_callbacks_ = &callbacks; - } - void initializeWriteFilterCallbacks(Network::WriteFilterCallbacks& callbacks) override { - write_callbacks_ = &callbacks; - } - -private: - // Writes node metadata in write pipeline of the filter chain. - // Also, sets node metadata in Dynamic Metadata to be available for subsequent - // filters. - void writeNodeMetadata(); - - // Tries to read inital proxy header in the data bytes. - void tryReadInitialProxyHeader(Buffer::Instance& data); - - // Tries to read data after initial proxy header. This is currently in the - // form of google::protobuf::any which encapsulates google::protobuf::struct. - void tryReadProxyData(Buffer::Instance& data); - - // Helper function to share the metadata with other filters. - void updatePeer(const Istio::Common::WorkloadMetadataObject& obj, FilterDirection direction); - void updatePeer(const Istio::Common::WorkloadMetadataObject& obj); - - // Helper function to get metadata id. - std::string getMetadataId(); - - // Helper function to set filterstate when no client mxc found. - void setMetadataNotFoundFilterState(); - - // Config for MetadataExchange filter. - MetadataExchangeConfigSharedPtr config_; - // LocalInfo instance. - const LocalInfo::LocalInfo& local_info_; - // Read callback instance. - Network::ReadFilterCallbacks* read_callbacks_{}; - // Write callback instance. - Network::WriteFilterCallbacks* write_callbacks_{}; - // Stores the length of proxy data that contains node metadata. - uint64_t proxy_data_length_{0}; - - const std::string ExchangeMetadataHeader = "x-envoy-peer-metadata"; - const std::string ExchangeMetadataHeaderId = "x-envoy-peer-metadata-id"; - - // Type url of google::protobuf::struct. - const std::string StructTypeUrl = "type.googleapis.com/google.protobuf.Struct"; - - // Captures the state machine of what is going on in the filter. - enum { - ConnProtocolNotRead, // Connection Protocol has not been read yet - WriteMetadata, // Write node metadata - ReadingInitialHeader, // MetadataExchangeInitialHeader is being read - ReadingProxyHeader, // Proxy Header is being read - NeedMoreDataInitialHeader, // Need more data to be read - NeedMoreDataProxyHeader, // Need more data to be read - Done, // Alpn Protocol Found and all the read is done - Invalid, // Invalid state, all operations fail - } conn_state_; -}; - -} // namespace MetadataExchange -} // namespace Tcp -} // namespace Envoy diff --git a/source/extensions/filters/network/metadata_exchange/metadata_exchange_initial_header.cc b/source/extensions/filters/network/metadata_exchange/metadata_exchange_initial_header.cc deleted file mode 100644 index 2a3124fe41c..00000000000 --- a/source/extensions/filters/network/metadata_exchange/metadata_exchange_initial_header.cc +++ /dev/null @@ -1,26 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "source/extensions/filters/network/metadata_exchange/metadata_exchange_initial_header.h" - -namespace Envoy { -namespace Tcp { -namespace MetadataExchange { - -const uint32_t MetadataExchangeInitialHeader::magic_number; - -} // namespace MetadataExchange -} // namespace Tcp -} // namespace Envoy diff --git a/source/extensions/filters/network/metadata_exchange/metadata_exchange_initial_header.h b/source/extensions/filters/network/metadata_exchange/metadata_exchange_initial_header.h deleted file mode 100644 index 009f5e51712..00000000000 --- a/source/extensions/filters/network/metadata_exchange/metadata_exchange_initial_header.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "envoy/common/platform.h" - -namespace Envoy { -namespace Tcp { -namespace MetadataExchange { - -// Used with MetadataExchangeHeaderProto to be extensible. -PACKED_STRUCT(struct MetadataExchangeInitialHeader { - uint32_t magic; // Magic number in network byte order. Most significant byte - // is placed first. - static const uint32_t magic_number = 0x3D230467; // decimal 1025705063 - uint32_t data_size; // Size of the data blob in network byte order. Most - // significant byte is placed first. -}); - -} // namespace MetadataExchange -} // namespace Tcp -} // namespace Envoy \ No newline at end of file diff --git a/source/extensions/filters/network/metadata_exchange/metadata_exchange_test.cc b/source/extensions/filters/network/metadata_exchange/metadata_exchange_test.cc deleted file mode 100644 index 7b59982b379..00000000000 --- a/source/extensions/filters/network/metadata_exchange/metadata_exchange_test.cc +++ /dev/null @@ -1,158 +0,0 @@ -/* Copyright 2019 Istio Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "source/extensions/filters/network/metadata_exchange/metadata_exchange.h" - -#include "gmock/gmock.h" -#include "google/protobuf/util/message_differencer.h" -#include "gtest/gtest.h" -#include "source/common/buffer/buffer_impl.h" -#include "source/common/protobuf/protobuf.h" -#include "source/extensions/filters/network/metadata_exchange/metadata_exchange_initial_header.h" -#include "test/mocks/local_info/mocks.h" -#include "test/mocks/network/mocks.h" -#include "test/mocks/protobuf/mocks.h" -#include "test/mocks/server/server_factory_context.h" - -using ::google::protobuf::util::MessageDifferencer; -using testing::NiceMock; -using testing::Return; -using testing::ReturnRef; - -namespace Envoy { -namespace Tcp { -namespace MetadataExchange { -namespace { - -MATCHER_P(MapEq, rhs, "") { return MessageDifferencer::Equals(arg, rhs); } - -void ConstructProxyHeaderData(::Envoy::Buffer::OwnedImpl& serialized_header, - Envoy::Protobuf::Any& proxy_header, - MetadataExchangeInitialHeader* initial_header) { - std::string serialized_proxy_header = proxy_header.SerializeAsString(); - memset(initial_header, 0, sizeof(MetadataExchangeInitialHeader)); - initial_header->magic = absl::ghtonl(MetadataExchangeInitialHeader::magic_number); - initial_header->data_size = absl::ghtonl(serialized_proxy_header.length()); - serialized_header.add(::Envoy::Buffer::OwnedImpl{absl::string_view( - reinterpret_cast(initial_header), sizeof(MetadataExchangeInitialHeader))}); - serialized_header.add(::Envoy::Buffer::OwnedImpl{serialized_proxy_header}); -} - -} // namespace - -class MetadataExchangeFilterTest : public testing::Test { -public: - MetadataExchangeFilterTest() { ENVOY_LOG_MISC(info, "test"); } - - void initialize() { initialize(absl::flat_hash_set()); } - - void initialize(absl::flat_hash_set additional_labels) { - config_ = std::make_shared( - stat_prefix_, "istio2", FilterDirection::Downstream, false, additional_labels, context_, - *scope_.rootScope()); - filter_ = std::make_unique(config_, local_info_); - filter_->initializeReadFilterCallbacks(read_filter_callbacks_); - filter_->initializeWriteFilterCallbacks(write_filter_callbacks_); - metadata_node_.set_id("test"); - auto node_metadata_map = metadata_node_.mutable_metadata()->mutable_fields(); - (*node_metadata_map)["namespace"].set_string_value("default"); - (*node_metadata_map)["labels"].set_string_value("{app, details}"); - EXPECT_CALL(read_filter_callbacks_.connection_, streamInfo()) - .WillRepeatedly(ReturnRef(stream_info_)); - EXPECT_CALL(local_info_, node()).WillRepeatedly(ReturnRef(metadata_node_)); - } - - void initializeStructValues() { - (*details_value_.mutable_fields())["namespace"].set_string_value("default"); - (*details_value_.mutable_fields())["labels"].set_string_value("{app, details}"); - - (*productpage_value_.mutable_fields())["namespace"].set_string_value("default"); - (*productpage_value_.mutable_fields())["labels"].set_string_value("{app, productpage}"); - } - - NiceMock context_; - Envoy::Protobuf::Struct details_value_; - Envoy::Protobuf::Struct productpage_value_; - MetadataExchangeConfigSharedPtr config_; - std::unique_ptr filter_; - Stats::IsolatedStoreImpl scope_; - std::string stat_prefix_{"test.metadataexchange"}; - NiceMock read_filter_callbacks_; - NiceMock write_filter_callbacks_; - Network::MockConnection connection_; - NiceMock local_info_; - NiceMock stream_info_; - envoy::config::core::v3::Node metadata_node_; -}; - -TEST_F(MetadataExchangeFilterTest, MetadataExchangeFound) { - initialize(); - initializeStructValues(); - - EXPECT_CALL(read_filter_callbacks_.connection_, nextProtocol()).WillRepeatedly(Return("istio2")); - - ::Envoy::Buffer::OwnedImpl data; - MetadataExchangeInitialHeader initial_header; - Envoy::Protobuf::Any productpage_any_value; - productpage_any_value.set_type_url("type.googleapis.com/google.protobuf.Struct"); - *productpage_any_value.mutable_value() = productpage_value_.SerializeAsString(); - ConstructProxyHeaderData(data, productpage_any_value, &initial_header); - ::Envoy::Buffer::OwnedImpl world{"world"}; - data.add(world); - - EXPECT_EQ(Envoy::Network::FilterStatus::Continue, filter_->onData(data, false)); - EXPECT_EQ(data.toString(), "world"); - - EXPECT_EQ(0UL, config_->stats().initial_header_not_found_.value()); - EXPECT_EQ(0UL, config_->stats().header_not_found_.value()); - EXPECT_EQ(1UL, config_->stats().alpn_protocol_found_.value()); -} - -TEST_F(MetadataExchangeFilterTest, MetadataExchangeAdditionalLabels) { - initialize({"role"}); - initializeStructValues(); - - EXPECT_CALL(read_filter_callbacks_.connection_, nextProtocol()).WillRepeatedly(Return("istio2")); - - ::Envoy::Buffer::OwnedImpl data; - MetadataExchangeInitialHeader initial_header; - Envoy::Protobuf::Any productpage_any_value; - productpage_any_value.set_type_url("type.googleapis.com/google.protobuf.Struct"); - *productpage_any_value.mutable_value() = productpage_value_.SerializeAsString(); - ConstructProxyHeaderData(data, productpage_any_value, &initial_header); - ::Envoy::Buffer::OwnedImpl world{"world"}; - data.add(world); - - EXPECT_EQ(Envoy::Network::FilterStatus::Continue, filter_->onData(data, false)); - EXPECT_EQ(data.toString(), "world"); - - EXPECT_EQ(0UL, config_->stats().initial_header_not_found_.value()); - EXPECT_EQ(0UL, config_->stats().header_not_found_.value()); - EXPECT_EQ(1UL, config_->stats().alpn_protocol_found_.value()); -} - -TEST_F(MetadataExchangeFilterTest, MetadataExchangeNotFound) { - initialize(); - - EXPECT_CALL(read_filter_callbacks_.connection_, nextProtocol()).WillRepeatedly(Return("istio")); - - ::Envoy::Buffer::OwnedImpl data{}; - EXPECT_EQ(Envoy::Network::FilterStatus::Continue, filter_->onData(data, false)); - EXPECT_EQ(1UL, config_->stats().alpn_protocol_not_found_.value()); -} - -} // namespace MetadataExchange -} // namespace Tcp -} // namespace Envoy diff --git a/test/envoye2e/driver/xds.go b/test/envoye2e/driver/xds.go index 05f3d4f3bc2..ae9bdb2d622 100644 --- a/test/envoye2e/driver/xds.go +++ b/test/envoye2e/driver/xds.go @@ -33,7 +33,7 @@ import ( "github.com/envoyproxy/go-control-plane/pkg/server/v3" "google.golang.org/grpc" - "istio.io/proxy/test/envoye2e/workloadapi" + workloadapi "github.com/envoyproxy/go-control-plane/contrib/envoy/extensions/filters/common/workload_discovery/v3" ) // XDS creates an xDS server diff --git a/test/envoye2e/env/wasm.go b/test/envoye2e/env/wasm.go index 6cac1b03ee7..6671c6916e8 100644 --- a/test/envoye2e/env/wasm.go +++ b/test/envoye2e/env/wasm.go @@ -48,6 +48,11 @@ func EnsureWasmFiles(t *testing.T) { if err != nil { t.Fatal(err) } + // Create the directory if it does not exist + err = os.MkdirAll(filepath.Dir(file), 0o755) + if err != nil { + t.Fatal(err) + } err = os.WriteFile(file, content, 0o666) if err != nil { t.Fatal(err) diff --git a/tools/gen_compilation_database.py b/tools/gen_compilation_database.py deleted file mode 100755 index a195ca98adf..00000000000 --- a/tools/gen_compilation_database.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python3 -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import argparse -import json -import os -import shlex -import subprocess -from pathlib import Path - - -# This method is equivalent to https://github.com/grailbio/bazel-compilation-database/blob/master/generate.py -def generate_compilation_database(args): - # We need to download all remote outputs for generated source code. This option lives here to override those - # specified in bazelrc. - bazel_startup_options = shlex.split(os.environ.get("BAZEL_STARTUP_OPTION_LIST", "")) - bazel_options = shlex.split(os.environ.get("BAZEL_BUILD_OPTION_LIST", "")) + [ - "--config=compdb", - "--remote_download_outputs=all", - ] - - source_dir_targets = args.bazel_targets - - subprocess.check_call(["bazel", *bazel_startup_options, "build"] + bazel_options + [ - "--aspects=@bazel_compdb//:aspects.bzl%compilation_database_aspect", - "--output_groups=compdb_files,header_files" - ] + source_dir_targets) - - execroot = subprocess.check_output( - ["bazel", *bazel_startup_options, "info", *bazel_options, - "execution_root"]).decode().strip() - - db_entries = [] - for db in Path(execroot).glob('**/*.compile_commands.json'): - db_entries.extend(json.loads(db.read_text())) - - def replace_execroot_marker(db_entry): - if 'directory' in db_entry and db_entry['directory'] == '__EXEC_ROOT__': - db_entry['directory'] = execroot - if 'command' in db_entry: - db_entry['command'] = ( - db_entry['command'].replace('-isysroot __BAZEL_XCODE_SDKROOT__', '')) - return db_entry - - return list(map(replace_execroot_marker, db_entries)) - - -def is_header(filename): - for ext in (".h", ".hh", ".hpp", ".hxx"): - if filename.endswith(ext): - return True - return False - - -def is_compile_target(target, args): - filename = target["file"] - if is_header(filename): - if args.include_all: - return True - if not args.include_headers: - return False - - if filename.startswith("bazel-out/"): - if args.include_all: - return True - if not args.include_genfiles: - return False - - if filename.startswith("external/"): - if args.include_all: - return True - if not args.include_external: - return False - - return True - - -def modify_compile_command(target, args): - cc, options = target["command"].split(" ", 1) - - # Workaround for bazel added C++11 options, those doesn't affect build itself but - # clang-tidy will misinterpret them. - options = options.replace("-std=c++0x ", "") - options = options.replace("-std=c++11 ", "") - - if args.vscode: - # Visual Studio Code doesn't seem to like "-iquote". Replace it with - # old-style "-I". - options = options.replace("-iquote ", "-I ") - - if args.system_clang: - if cc.find("clang"): - cc = "clang++" - - if is_header(target["file"]): - options += " -Wno-pragma-once-outside-header -Wno-unused-const-variable" - options += " -Wno-unused-function" - # By treating external/envoy* as C++ files we are able to use this script from subrepos that - # depend on Envoy targets. - if not target["file"].startswith("external/") or target["file"].startswith( - "external/envoy"): - # *.h file is treated as C header by default while our headers files are all C++20. - options = "-x c++ -std=c++20 -fexceptions " + options - - target["command"] = " ".join([cc, options]) - return target - - -def fix_compilation_database(args, db): - db = [modify_compile_command(target, args) for target in db if is_compile_target(target, args)] - - with open("compile_commands.json", "w") as db_file: - json.dump(db, db_file, indent=2) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Generate JSON compilation database') - parser.add_argument('--include_external', action='store_true') - parser.add_argument('--include_genfiles', action='store_true') - parser.add_argument('--include_headers', action='store_true') - parser.add_argument('--vscode', action='store_true') - parser.add_argument('--include_all', action='store_true') - parser.add_argument( - '--system-clang', - action='store_true', - help= - 'Use `clang++` instead of the bazel wrapper for commands. This may help if `clangd` cannot find/run the tools.' - ) - parser.add_argument( - 'bazel_targets', nargs='*', default=[ - "//source/...", - ]) - args = parser.parse_args() - fix_compilation_database(args, generate_compilation_database(args)) diff --git a/tools/vscode/refresh_compdb.sh b/tools/vscode/refresh_compdb.sh deleted file mode 100755 index 0c516d3d844..00000000000 --- a/tools/vscode/refresh_compdb.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash -# Copyright Istio Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -opts=(--vscode) - -# Setting TEST_TMPDIR here so the compdb headers won't be overwritten by another bazel run -TEST_TMPDIR=${BUILD_DIR:-/tmp}/envoy-compdb tools/gen_compilation_database.py \ - "${opts[@]}" - -# Kill clangd to reload the compilation database -pkill clangd || :