Compare commits

..

67 Commits

Author SHA1 Message Date
Istio Automation 06da92fd5c
Automator: update envoy@ in istio/proxy@release-1.19 (#5440) 2024-04-04 17:57:35 -07:00
Istio Automation 9b2eb4ea8c
Automator: update envoy@ in istio/proxy@release-1.19 (#5431) 2024-04-02 19:07:07 -07:00
Istio Automation 7160620e52
Automator: update envoy@ in istio/proxy@release-1.19 (#5388) 2024-03-06 16:14:26 -08:00
Istio Automation 1b19a2a500
Automator: update common-files@release-1.19 in istio/proxy@release-1.19 (#5386) 2024-03-05 23:34:13 -08:00
Istio Automation 1eeb3a6180
Automator: update envoy@ in istio/proxy@release-1.19 (#5380) 2024-03-05 14:29:20 -08:00
Istio Automation 1716984e9e
Automator: update envoy@ in istio/proxy@release-1.19 (#5374) 2024-03-03 16:35:37 -08:00
Istio Automation 71e077cbc1
fips: force stackdriver to use TLSv1.2 (#5342)
Change-Id: Icd1cd577c039512bb90234642719a8b5d3523567

Signed-off-by: Kuat Yessenov <kuat@google.com>
Co-authored-by: Kuat Yessenov <kuat@google.com>
2024-02-17 03:26:51 -08:00
Istio Automation df03234fbb
Automator: update envoy@ in istio/proxy@release-1.19 (#5330) 2024-02-14 06:39:25 -08:00
Istio Automation 188e4b578d
Automator: update envoy@ in istio/proxy@release-1.19 (#5318) 2024-02-11 01:41:53 -08:00
Istio Automation 5f73db68b7
Automator: update common-files@release-1.19 in istio/proxy@release-1.19 (#5316) 2024-02-09 20:49:54 -08:00
Istio Automation 71cd58c6ac
Automator: update envoy@ in istio/proxy@release-1.19 (#5309) 2024-02-08 07:07:25 -08:00
Istio Automation ae80069bcb
Automator: update envoy@ in istio/proxy@release-1.19 (#5271) 2024-01-18 15:25:48 -08:00
Istio Automation 3cdaab0719
Automator: update common-files@release-1.19 in istio/proxy@release-1.19 (#5256) 2024-01-09 19:44:33 -08:00
Istio Automation 38b08e6452
Automator: update envoy@ in istio/proxy@release-1.19 (#5253) 2024-01-09 14:12:43 -08:00
Istio Automation 802428003a
Automator: update envoy@ in istio/proxy@release-1.19 (#5245) 2024-01-04 19:47:58 -08:00
Istio Automation dd4d22e6eb
Automator: update envoy@ in istio/proxy@release-1.19 (#5240) 2024-01-03 20:59:06 -08:00
Istio Automation 97afdc41e0
stats: fix memory leak in periodic reporting (#5221)
Change-Id: Ibd473bb14c51786c34f2d459ce5e1eea675e3d13

Signed-off-by: Kuat Yessenov <kuat@google.com>
Co-authored-by: Kuat Yessenov <kuat@google.com>
2023-12-20 00:06:38 -08:00
Istio Automation eb790e43d4
Automator: update envoy@ in istio/proxy@release-1.19 (#5216) 2023-12-19 17:57:48 -08:00
Istio Automation 7fc1e3be58
Automator: update envoy@ in istio/proxy@release-1.19 (#5211) 2023-12-16 08:20:47 -08:00
Istio Automation d475eda090
Automator: update common-files@release-1.19 in istio/proxy@release-1.19 (#5191) 2023-12-05 18:53:59 -08:00
zirain 37bf2b4817
enable zstd (#5179) 2023-11-29 18:27:15 -08:00
Istio Automation 25a358d473
Automator: update envoy@ in istio/proxy@release-1.19 (#5162) 2023-11-21 16:01:50 -08:00
Istio Automation 74d77fd3aa
Automator: update envoy@ in istio/proxy@release-1.19 (#5152) 2023-11-16 14:07:05 -08:00
zirain d839f09467
enable grpc_field_extraction filter (#5147) 2023-11-14 21:47:03 -08:00
Istio Automation f9707e29aa
Automator: update envoy@ in istio/proxy@release-1.19 (#5136) 2023-11-08 14:08:04 -08:00
Istio Automation c72a000d15
Automator: update common-files@release-1.19 in istio/proxy@release-1.19 (#5132) 2023-11-07 20:33:32 -08:00
Istio Automation 6709b394a4
fix update_envoy.sh (#5106)
Co-authored-by: zirain <zirain2009@gmail.com>
2023-10-31 09:01:29 -07:00
Istio Automation fb5e034fbc
Automator: update envoy@ in istio/proxy@release-1.19 (#5100) 2023-10-28 12:28:30 -07:00
Istio Automation 2e47601ff2
Automator: update envoy@ in istio/proxy@release-1.19 (#5096) 2023-10-25 15:03:27 -07:00
Istio Automation 6a145a6ace
Automator: update envoy@ in istio/proxy@release-1.19 (#5089) 2023-10-25 00:28:44 -07:00
Istio Automation 54f8fc4f97
[release-1.19] turn previously removed BAZEL_BIN_PATH in to target-specific variable to fix exportcache builds (#5091)
* add BAZEL_BIN_PATH back to fix builds

* add a target-specific variable for exportcache to set BAZEL_BIN_PATH only when calling the exportcache target

* remove the earlier definition of BAZEL_BIN_PATH

---------

Co-authored-by: Skyler Spaeth <skyler_spaeth@rapid7.com>
2023-10-24 18:27:43 -07:00
Istio Automation 517cd96513
Automator: update envoy@ in istio/proxy@release-1.19 (#5083) 2023-10-24 07:27:43 -07:00
Istio Automation a804636e5c
Automator: update envoy@ in istio/proxy@release-1.19 (#5067) 2023-10-19 20:48:27 -07:00
Istio Automation 9314f9ad2d
Automator: update envoy@ in istio/proxy@release-1.19 (#5060) 2023-10-19 01:24:09 -07:00
Istio Automation 33e880887a
Automator: update envoy@ in istio/proxy@release-1.19 (#5055) 2023-10-17 20:49:38 -07:00
Istio Automation cfed93480a
Automator: update envoy@ in istio/proxy@release-1.19 (#5049) 2023-10-16 15:01:38 -07:00
Istio Automation f413882189
Automator: update envoy@ in istio/proxy@release-1.19 (#5042) 2023-10-14 07:30:35 -07:00
Istio Automation 96f989cc74
enable connect_grpc_bridge filter (#5037)
Co-authored-by: zirain <zirain2009@gmail.com>
2023-10-14 06:05:34 -07:00
Istio Automation 11647e18d3
Automator: update envoy@ in istio/proxy@release-1.19 (#5033) 2023-10-12 20:29:33 -07:00
Istio Automation bdb31c9666
Automator: update envoy@ in istio/proxy@release-1.19 (#5030) 2023-10-11 17:28:41 -07:00
Istio Automation 4d518f8d51
add file system http cache filter (#5014)
Co-authored-by: zirain <zirain2009@gmail.com>
2023-10-10 18:49:30 -07:00
Istio Automation e9b7643ac1
Automator: update common-files@release-1.19 in istio/proxy@release-1.19 (#5024) 2023-10-10 17:44:56 -07:00
Istio Automation a1ff538a63
Automator: update envoy@ in istio/proxy@release-1.19 (#5019) 2023-10-10 14:54:46 -07:00
Istio Automation 0f19b5a369
Automator: update envoy@ in istio/proxy@release-1.19 (#5007) 2023-10-09 20:19:17 -07:00
Istio Automation 2891ae0979
Automator: update envoy@ in istio/proxy@release-1.19 (#4999) 2023-10-07 18:37:20 -07:00
Istio Automation ec466124d7
Automator: update common-files@release-1.19 in istio/proxy@release-1.19 (#5000) 2023-10-06 18:52:18 -07:00
Kuat ed82e4b0fb
http mx: fix regression (#4990) (#4991)
* http mx: fix regression



* update test



---------

Signed-off-by: Kuat Yessenov <kuat@google.com>
2023-10-06 18:12:48 -07:00
Istio Automation db2e94f010
Automator: update envoy@ in istio/proxy@release-1.19 (#4986) 2023-10-03 20:44:35 -07:00
Istio Automation 103b0ac8fc
Automator: update envoy@ in istio/proxy@release-1.19 (#4963) 2023-09-20 18:54:02 -07:00
Istio Automation 0434620cc1
Automator: update common-files@release-1.19 in istio/proxy@release-1.19 (#4936) 2023-09-07 00:16:55 -07:00
Greg Hanson 3b92829545
do not send mx headers for services out of mesh (#4895) (#4933)
* do not send mx headers for services out of mesh

* remove env var feature toggle, update config.proto

* move skip_external_clusters to IstioHeaders

* use bool value in cluster metadata

* formatting fixes

* add unit tests

* add unit tets for PassthroughCluster

* code review comments
2023-09-06 19:14:54 -07:00
Istio Automation 3ac8014570
Automator: update envoy@ in istio/proxy@release-1.19 (#4923) 2023-08-31 18:34:55 -07:00
Istio Automation 17c173b8ad
Automator: update envoy@ in istio/proxy@release-1.19 (#4913) 2023-08-25 06:44:57 -07:00
Istio Automation 31cf02691e
Automator: update envoy@ in istio/proxy@release-1.19 (#4906) 2023-08-22 20:04:51 -07:00
Istio Automation e78f346b66
Automator: update envoy@ in istio/proxy@release-1.19 (#4903) 2023-08-21 18:56:48 -07:00
Istio Automation c41b644613
Automator: update common-files@release-1.19 in istio/proxy@release-1.19 (#4898) 2023-08-17 21:10:04 -07:00
Istio Automation e6816f641a
Fix skipping ALPN overwriting (#4890)
Signed-off-by: Jacek Ewertowski <jewertow@redhat.com>
Co-authored-by: Jacek Ewertowski <jewertow@redhat.com>
2023-08-11 03:32:04 -07:00
Istio Automation b954956ba1
[release-1.19] metadata_exchange: fix layered TCP/HTTP write conflict (#4887)
* metadata_exchange: fix layered TCP/HTTP write conflict

Signed-off-by: Kuat Yessenov <kuat@google.com>

* add test

Signed-off-by: Kuat Yessenov <kuat@google.com>

---------

Signed-off-by: Kuat Yessenov <kuat@google.com>
Co-authored-by: Kuat Yessenov <kuat@google.com>
2023-08-10 18:48:03 -07:00
Istio Automation beb639e84a
[release-1.19] metadata_exchange: stop waiting for data when upstream closes (#4880)
* metadata_exchange: stop waiting for data when upstream closes

* test/envoye2e: add errcheck in tcp driver

* metadata_exchange: reduce log severity in abort case

---------

Co-authored-by: Brenden Blanco <bblanco@vmware.com>
2023-08-09 19:37:03 -07:00
Istio Automation 01b89b4c43
Automator: update common-files@release-1.19 in istio/proxy@release-1.19 (#4884) 2023-08-09 12:32:32 -07:00
Istio Automation 76e9677fb1
metadata exchange: fix cache data race (#4877)
Signed-off-by: Kuat Yessenov <kuat@google.com>
Co-authored-by: Kuat Yessenov <kuat@google.com>
2023-08-08 00:21:05 -07:00
Kalya Subramanian da2487b4c8
Use go-control-plane from last 1.27 cut (#4872)
Signed-off-by: Kalya Subramanian <kasubra@microsoft.com>
2023-08-07 19:38:04 -07:00
Istio Automation 6a20765e1f
Automator: update common-files@release-1.19 in istio/proxy@release-1.19 (#4873) 2023-08-07 12:46:04 -07:00
Istio Automation d2bde2ec1b
Automator: update common-files@release-1.19 in istio/proxy@release-1.19 (#4867) 2023-08-04 11:07:09 -07:00
Istio Automation 467917862c
sync health_checkers extensions (#4863)
Co-authored-by: zirain <zirain2009@gmail.com>
2023-08-03 21:17:07 -07:00
Istio Automation 16a9e0b7fb
Automator: update envoy@ in istio/proxy@release-1.19 (#4864) 2023-08-03 17:32:07 -07:00
Eric Van Norman c5b8bd373e
[release-1.19] Automated branching step 4 (#4860) 2023-08-03 14:40:08 -07:00
331 changed files with 24205 additions and 6177 deletions

View File

@ -13,12 +13,6 @@ build:remote --remote_timeout=7200
# Istio specific Bazel build/test options.
# ========================================
# Enable libc++ and C++20 by default.
build:linux --config=clang
# put /usr/local/bin before /usr/bin to avoid picking up wrong python3.6 when building envoy.tls.key_providers.cryptomb
build:linux --action_env=PATH=/usr/lib/llvm/bin:/usr/local/bin:/bin:/usr/bin
# Need for CI image to pickup docker-credential-gcloud, PATH is fixed in rbe-toolchain-* configs.
build:remote-ci --action_env=PATH=/usr/local/google-cloud-sdk/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/llvm/bin
@ -26,7 +20,9 @@ build:remote-ci --action_env=PATH=/usr/local/google-cloud-sdk/bin:/usr/sbin:/usr
# See: https://github.com/envoyproxy/envoy/pull/6519
build --define path_normalization_by_default=true
build:macos --define tcmalloc=disabled
# Heap profiler is supported only with gperf tcmalloc, not google tcmalloc.
# See: https://github.com/istio/istio/issues/28233
build --define tcmalloc=gperftools
# Build with embedded V8-based WebAssembly runtime.
build --define wasm=v8
@ -50,22 +46,5 @@ build:debug -c dbg
build --cxxopt -Wformat
build --cxxopt -Wformat-security
build:clang --host_action_env=CC=
build:clang --host_action_env=CXX=
# CI sanitizer configuration
#
build:clang-asan-ci --config=asan
build:clang-asan-ci --linkopt='-L/usr/lib/llvm/lib/x86_64-unknown-linux-gnu'
build:clang-asan-ci --linkopt='-Wl,-rpath,/usr/lib/llvm/lib/x86_64-unknown-linux-gnu'
build:clang-asan-ci --linkopt='-L/usr/lib/llvm/lib/clang/14.0.0/lib/x86_64-unknown-linux-gnu'
build:clang-tsan-ci --config=tsan
build:clang-tsan-ci --linkopt=-L/opt/libcxx_tsan/lib
build:clang-tsan-ci --linkopt=-Wl,-rpath,/opt/libcxx_tsan/lib
# get from https://github.com/Homebrew/homebrew-core/blob/master/Formula/e/envoy.rb
build:macos --cxxopt=-Wno-range-loop-analysis
build:macos --host_cxxopt=-Wno-range-loop-analysis
build:macos --cxxopt=-Wno-deprecated-declarations
build:macos --host_cxxopt=-Wno-deprecated-declarations
# Link pthread for flatbuffers
build --host_linkopt=-pthread

View File

@ -1 +1 @@
7.6.0
6.3.2

View File

@ -1,6 +1,6 @@
{
"name": "istio build-tools",
"image": "gcr.io/istio-testing/build-tools-proxy:master-8e6480403f5cf4c9a4cd9d65174d01850e632e1a",
"image": "gcr.io/istio-testing/build-tools:release-1.19-013f27a57a64f1d22f60c3dcb9b242cf4814065a",
"privileged": true,
"remoteEnv": {
"USE_GKE_GCLOUD_AUTH_PLUGIN": "True",
@ -11,23 +11,5 @@
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {},
"ghcr.io/mpriscella/features/kind:1": {}
},
"customizations": {
"vscode": {
"extensions": [
"golang.go",
"rust-lang.rust-analyzer",
"eamodio.gitlens",
"zxh404.vscode-proto3",
"ms-azuretools.vscode-docker",
"redhat.vscode-yaml",
"IBM.output-colorizer"
],
"settings": {
"files.eol": "\n",
"go.useLanguageServer": true,
"go.lintTool": "golangci-lint"
}
}
}
}

1
.gitattributes vendored
View File

@ -8,7 +8,6 @@
*.gen.yaml linguist-generated=true
*.gen.json linguist-generated=true
*_pb2.py linguist-generated=true
manifests/charts/**/profile*.yaml linguist-generated=true
go.sum merge=union
vendor/** linguist-vendored
common/** linguist-vendored

View File

@ -1,14 +0,0 @@
# Configures Depdendabot to PR go security updates only
version: 2
updates:
# Go configuration for master branch
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
# Limit number of open PRs to 0 so that we only get security updates
# See https://docs.github.com/en/code-security/dependabot/dependabot-security-updates/configuring-dependabot-security-updates
open-pull-requests-limit: 0
labels:
- "release-notes-none"

6
.gitignore vendored
View File

@ -12,5 +12,9 @@ test/envoye2e/tcp_metadata_exchange/testoutput
test/envoye2e/http_metadata_exchange/testoutput
*.wasm
.vscode
/extensions/common/flatbuffers
/extensions/common/node_info_generated.h
/extensions/common/node_info_bfbs_generated.h
/extensions/common/proxy_expr.h
/extensions/common/nlohmann_json.hpp
out/
.cache

37
BUILD
View File

@ -1,8 +1,3 @@
load(
"@envoy//bazel:envoy_build_system.bzl",
"envoy_cc_binary",
)
# Copyright 2016 Istio Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@ -20,6 +15,10 @@ load(
################################################################################
#
load("@rules_pkg//:pkg.bzl", "pkg_tar")
load(
"@envoy//bazel:envoy_build_system.bzl",
"envoy_cc_binary",
)
exports_files(["LICENSE"])
@ -28,20 +27,29 @@ config_setting(
values = {
"cpu": "darwin",
},
visibility = ["//visibility:public"],
)
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 + [
visibility = ["//visibility:public"],
deps = [
"//extensions/access_log_policy:access_log_policy_lib",
"//extensions/metadata_exchange:metadata_exchange_lib",
"//extensions/stackdriver:stackdriver_plugin",
"//source/extensions/common/workload_discovery:api_lib", # Experimental: WIP
"//source/extensions/filters/http/alpn:config_lib",
"//source/extensions/filters/http/authn:filter_lib",
"//source/extensions/filters/http/connect_authority", # Experimental: ambient
"//source/extensions/filters/http/istio_stats",
"//source/extensions/filters/http/peer_metadata:filter_lib",
"//source/extensions/filters/listener/set_internal_dst_address:filter_lib", # Experimental: ambient
"//source/extensions/filters/network/forward_downstream_sni:config_lib",
"//source/extensions/filters/network/istio_authn:config_lib",
"//source/extensions/filters/network/metadata_exchange:config_lib",
"//source/extensions/filters/network/sni_verifier:config_lib",
"//source/extensions/filters/network/tcp_cluster_rewrite:config_lib",
"@envoy//source/exe:envoy_main_entry_lib",
],
)
@ -53,4 +61,5 @@ pkg_tar(
mode = "0755",
package_dir = "/usr/local/bin/",
tags = ["manual"],
visibility = ["//visibility:public"],
)

View File

@ -1 +1 @@
* @istio/wg-policies-and-telemetry-maintainers
* @istio/release-managers-1-19

View File

@ -7,16 +7,3 @@ to find out how you can help.
## Prerequisites
To make sure you're ready to build Envoy, clone and follow the upstream Envoy [build instructions](https://github.com/envoyproxy/envoy/blob/main/bazel/README.md). Be sure to copy clang.bazelrc into this directory after running the setup_clang script. Confirm that your environment is ready to go by running `bazel build --config=clang --define=wasm=disabled :envoy` (in this directory).
## How to use a devcontainer
1. Change the image in .devcontainer.json to `build-tools-proxy` instead of `build-tools`
2. Open the directory in a container with the Remote - Containers extension
3. Install the following extensions:
- [clangd](https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd)
- [bazel-stack-vscode-cc](https://marketplace.visualstudio.com/items?itemName=StackBuild.bazel-stack-vscode-cc)
4. Update clangd and reload the container
5. Edit the new `bsv.cc.compdb.targets` workspace setting and set it to `//:envoy_tar`
6. Execute `Bazel/C++: Generate Compilation Database` within vscode
Note: if you have a remote bazel cache or something mounted in your build container for your normal proxy builds, you'll need to configure that in the devcontainer with runArgs.

View File

@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Copyright 2016-2022 Istio Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -19,7 +19,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
SHELL := /usr/bin/env bash
SHELL := /bin/bash
# allow optional per-repo overrides
-include Makefile.overrides.mk

View File

@ -17,7 +17,7 @@ TOP := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
SHELL := /bin/bash
BAZEL_STARTUP_ARGS ?=
BAZEL_BUILD_ARGS ?=
BAZEL_TARGETS ?= //...
BAZEL_TARGETS ?= //... -extensions:metadata_exchange.wasm
# Don't build Debian packages and Docker images in tests.
BAZEL_TEST_TARGETS ?= ${BAZEL_TARGETS}
E2E_TEST_TARGETS ?= $$(go list ./...)
@ -26,20 +26,35 @@ HUB ?=
TAG ?=
repo_dir := .
ifeq "$(origin CC)" "default"
CC := clang
endif
ifeq "$(origin CXX)" "default"
CXX := clang++
endif
PATH := /usr/lib/llvm-10/bin:$(PATH)
VERBOSE ?=
ifeq "$(VERBOSE)" "1"
BAZEL_STARTUP_ARGS := --client_debug $(BAZEL_STARTUP_ARGS)
BAZEL_BUILD_ARGS := -s --sandbox_debug --verbose_failures $(BAZEL_BUILD_ARGS)
endif
ifeq "$(origin WITH_LIBCXX)" "undefined"
WITH_LIBCXX := $(shell ($(CXX) --version | grep ^g++ >/dev/null && echo 0) || echo 1)
endif
ifeq "$(WITH_LIBCXX)" "1"
BAZEL_CONFIG = --config=libc++
else
BAZEL_CONFIG =
endif
UNAME := $(shell uname)
ifeq ($(UNAME),Linux)
BAZEL_CONFIG_DEV = $(BAZEL_CONFIG)
BAZEL_CONFIG_REL = $(BAZEL_CONFIG) --config=release
BAZEL_CONFIG_ASAN = $(BAZEL_CONFIG) --config=clang-asan-ci
BAZEL_CONFIG_TSAN = $(BAZEL_CONFIG) --config=clang-tsan-ci
BAZEL_CONFIG_ASAN = $(BAZEL_CONFIG) --config=clang-asan
BAZEL_CONFIG_TSAN = $(BAZEL_CONFIG) --config=clang-tsan
endif
ifeq ($(UNAME),Darwin)
BAZEL_CONFIG_DEV = # macOS always links against libc++
@ -53,6 +68,7 @@ TEST_ENVOY_TARGET ?= //:envoy
TEST_ENVOY_DEBUG ?= trace
build:
export PATH=$(PATH) CC=$(CC) CXX=$(CXX) && \
bazel $(BAZEL_STARTUP_ARGS) build $(BAZEL_BUILD_ARGS) $(BAZEL_CONFIG_CURRENT) -- $(BAZEL_TARGETS)
build_envoy: BAZEL_CONFIG_CURRENT = $(BAZEL_CONFIG_REL)
@ -67,7 +83,14 @@ build_envoy_asan: BAZEL_CONFIG_CURRENT = $(BAZEL_CONFIG_ASAN)
build_envoy_asan: BAZEL_TARGETS = //:envoy
build_envoy_asan: build
check_wasm:
build_wasm:
export PATH=$(PATH) CC=$(CC) CXX=$(CXX) && bazel $(BAZEL_STARTUP_ARGS) build $(BAZEL_BUILD_ARGS) $(BAZEL_CONFIG_REL) //extensions:metadata_exchange.wasm
export PATH=$(PATH) CC=$(CC) CXX=$(CXX) && bazel $(BAZEL_STARTUP_ARGS) build $(BAZEL_BUILD_ARGS) $(BAZEL_CONFIG_REL) @envoy//test/tools/wee8_compile:wee8_compile_tool
bazel-bin/external/envoy/test/tools/wee8_compile/wee8_compile_tool bazel-bin/extensions/metadata_exchange.wasm bazel-bin/extensions/metadata_exchange.compiled.wasm
# NOTE: build_wasm has to happen before build_envoy, since the integration test references bazel-bin symbol link for envoy binary,
# which will be overwritten if wasm build happens after envoy.
check_wasm: build_wasm build_envoy
@true
clean:
@ -75,7 +98,7 @@ clean:
.PHONY: gen-extensions-doc
gen-extensions-doc:
buf generate --path source/extensions/filters
buf generate --path extensions/
gen:
@scripts/gen-testdata.sh
@ -84,12 +107,13 @@ 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)
export PATH=$(PATH) CC=$(CC) CXX=$(CXX) && \
bazel $(BAZEL_STARTUP_ARGS) build $(BAZEL_BUILD_ARGS) $(BAZEL_CONFIG_CURRENT) $(TEST_ENVOY_TARGET)
if [ -n "$(BAZEL_TEST_TARGETS)" ]; then \
bazel $(BAZEL_STARTUP_ARGS) test $(BAZEL_BUILD_ARGS) $(BAZEL_CONFIG_CURRENT) -- $(BAZEL_TEST_TARGETS); \
export PATH=$(PATH) CC=$(CC) CXX=$(CXX) && bazel $(BAZEL_STARTUP_ARGS) test $(BAZEL_BUILD_ARGS) $(BAZEL_CONFIG_CURRENT) $(BAZEL_TEST_ARGS) -- $(BAZEL_TEST_TARGETS); \
fi
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); \
env ENVOY_DEBUG=$(TEST_ENVOY_DEBUG) ENVOY_PATH=$(shell bazel 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
test_asan: BAZEL_CONFIG_CURRENT = $(BAZEL_CONFIG_ASAN)
@ -105,24 +129,48 @@ 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
@scripts/verify-last-flag-matches-upstream.sh
protoc = protoc -I common-protos -I extensions
protoc_gen_docs_plugin := --docs_out=camel_case_fields=false,warnings=true,per_file=true,mode=html_fragment_with_front_matter:$(repo_dir)/
metadata_exchange_path := extensions/metadata_exchange
metadata_exchange_protos := $(wildcard $(metadata_exchange_path)/*.proto)
metadata_exchange_docs := $(metadata_exchange_protos:.proto=.pb.html)
$(metadata_exchange_docs): $(metadata_exchange_protos)
@$(protoc) -I ./extensions $(protoc_gen_docs_plugin)$(metadata_exchange_path) $^
stackdriver_path := extensions/stackdriver/config/v1alpha1
stackdriver_protos := $(wildcard $(stackdriver_path)/*.proto)
stackdriver_docs := $(stackdriver_protos:.proto=.pb.html)
$(stackdriver_docs): $(stackdriver_protos)
@$(protoc) -I ./extensions $(protoc_gen_docs_plugin)$(stackdriver_path) $^
accesslog_policy_path := extensions/access_log_policy/config/v1alpha1
accesslog_policy_protos := $(wildcard $(accesslog_policy_path)/*.proto)
accesslog_policy_docs := $(accesslog_policy_protos:.proto=.pb.html)
$(accesslog_policy_docs): $(accesslog_policy_protos)
@$(protoc) -I ./extensions $(protoc_gen_docs_plugin)$(accesslog_policy_path) $^
extensions-docs: $(metadata_exchange_docs) $(stackdriver_docs) $(accesslog_policy_docs)
test_release:
ifeq "$(shell uname -m)" "x86_64"
export BAZEL_BUILD_ARGS="$(BAZEL_BUILD_ARGS)" && ./scripts/release-binary.sh
export PATH=$(PATH) CC=$(CC) CXX=$(CXX) BAZEL_BUILD_ARGS="$(BAZEL_BUILD_ARGS)" && ./scripts/release-binary.sh
else
# Only x86 has support for legacy GLIBC, otherwise pass -i to skip the check
export BAZEL_BUILD_ARGS="$(BAZEL_BUILD_ARGS)" && ./scripts/release-binary.sh -i
export PATH=$(PATH) CC=$(CC) CXX=$(CXX) BAZEL_BUILD_ARGS="$(BAZEL_BUILD_ARGS)" && ./scripts/release-binary.sh -i
endif
push_release:
ifeq "$(shell uname -m)" "x86_64"
export BAZEL_BUILD_ARGS="$(BAZEL_BUILD_ARGS)" && ./scripts/release-binary.sh -d "$(RELEASE_GCS_PATH)" ${PUSH_RELEASE_FLAGS}
export PATH=$(PATH) CC=$(CC) CXX=$(CXX) BAZEL_BUILD_ARGS="$(BAZEL_BUILD_ARGS)" && ./scripts/release-binary.sh -d "$(RELEASE_GCS_PATH)" ${PUSH_RELEASE_FLAGS}
else
# Only x86 has support for legacy GLIBC, otherwise pass -i to skip the check
export BAZEL_BUILD_ARGS="$(BAZEL_BUILD_ARGS)" && ./scripts/release-binary.sh -i -d "$(RELEASE_GCS_PATH)" ${PUSH_RELEASE_FLAGS}
export PATH=$(PATH) CC=$(CC) CXX=$(CXX) BAZEL_BUILD_ARGS="$(BAZEL_BUILD_ARGS)" && ./scripts/release-binary.sh -i -d "$(RELEASE_GCS_PATH)" ${PUSH_RELEASE_FLAGS}
endif
# Used by build container to export the build output from the docker volume cache

View File

@ -2,7 +2,3 @@
The Istio Proxy is a microservice proxy that can be used on the client and server side, and forms a microservice mesh.
It is based on [Envoy](http://envoyproxy.io) with the addition of several policy and telemetry extensions.
According to the [conclusion from Istio workgroup meeting on 4-17-2024](https://docs.google.com/document/d/1wsa06GGiq1LEGwhkiPP0FKIZJqdAiue-VeBonWAzAyk/edit#heading=h.ma5hboh81yw):
- New extensions are not added unless they are part of core APIs

View File

@ -3,5 +3,5 @@
Here are some resources to help you understand and use Istio:
- For in-depth information about how to use Istio, visit [istio.io](https://istio.io)
- To ask questions and get assistance from our community, visit [GitHub Discussions](https://github.com/istio/istio/discussions)
- To learn how to participate in our overall community, visit [our community page](https://istio.io/latest/get-involved/)
- To ask questions and get assistance from our community, visit [discuss.istio.io](https://discuss.istio.io)
- To learn how to participate in our overall community, visit [our community page](https://istio.io/about/community)

View File

@ -18,14 +18,26 @@ workspace(name = "io_istio_proxy")
# http_archive is not a native function since bazel 0.19
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load(
"//bazel:repositories.bzl",
"docker_dependencies",
"istioapi_dependencies",
)
istioapi_dependencies()
bind(
name = "boringssl_crypto",
actual = "//external:ssl",
)
# 1. Determine SHA256 `wget https://github.com/envoyproxy/envoy/archive/$COMMIT.tar.gz && sha256sum $COMMIT.tar.gz`
# 2. Update .bazelversion, envoy.bazelrc and .bazelrc if needed.
#
# Commit date: 2025-07-10
ENVOY_SHA = "46bb6bc3dc41a684671fd4811eddfe82207a6d21"
# Commit date: 2024-04-04
ENVOY_SHA = "9134d6a65e5c2c714d503807eb31a8490471fc5f"
ENVOY_SHA256 = "e3ca4967f3cfd343cbf9983c7ec85eb1845c5d3b219e045cf008e730e9c06b61"
ENVOY_SHA256 = "a60eaa0e6a2ffac6428777c4fe716444dc5fef5e885b5c50ab646112d35fc95d"
ENVOY_ORG = "envoyproxy"
@ -73,3 +85,61 @@ install_deps()
load("@envoy//bazel:dependency_imports.bzl", "envoy_dependency_imports")
envoy_dependency_imports()
# Bazel @rules_pkg
http_archive(
name = "rules_pkg",
sha256 = "aeca78988341a2ee1ba097641056d168320ecc51372ef7ff8e64b139516a4937",
urls = [
"https://github.com/bazelbuild/rules_pkg/releases/download/0.2.6-1/rules_pkg-0.2.6.tar.gz",
"https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.2.6/rules_pkg-0.2.6.tar.gz",
],
)
load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies")
rules_pkg_dependencies()
# Docker dependencies
docker_dependencies()
load(
"@io_bazel_rules_docker//repositories:repositories.bzl",
container_repositories = "repositories",
)
container_repositories()
load("@io_bazel_rules_docker//repositories:deps.bzl", container_deps = "deps")
container_deps()
load(
"@io_bazel_rules_docker//container:container.bzl",
"container_pull",
)
container_pull(
name = "distroless_cc",
# Latest as of 10/21/2019. To update, remove this line, re-build, and copy the suggested digest.
digest = "sha256:86f16733f25964c40dcd34edf14339ddbb2287af2f7c9dfad88f0366723c00d7",
registry = "gcr.io",
repository = "distroless/cc",
)
container_pull(
name = "bionic",
# Latest as of 10/21/2019. To update, remove this line, re-build, and copy the suggested digest.
digest = "sha256:3e83eca7870ee14a03b8026660e71ba761e6919b6982fb920d10254688a363d4",
registry = "index.docker.io",
repository = "library/ubuntu",
tag = "bionic",
)
# End of docker dependencies
load("//bazel:wasm.bzl", "wasm_dependencies")
wasm_dependencies()

View File

@ -7,7 +7,6 @@ ENVOY_EXTENSIONS = {
"envoy.access_loggers.file": "//source/extensions/access_loggers/file:config",
"envoy.access_loggers.extension_filters.cel": "//source/extensions/access_loggers/filters/cel:config",
"envoy.access_loggers.fluentd" : "//source/extensions/access_loggers/fluentd:config",
"envoy.access_loggers.http_grpc": "//source/extensions/access_loggers/grpc:http_config",
"envoy.access_loggers.tcp_grpc": "//source/extensions/access_loggers/grpc:tcp_config",
"envoy.access_loggers.open_telemetry": "//source/extensions/access_loggers/open_telemetry:config",
@ -44,6 +43,7 @@ ENVOY_EXTENSIONS = {
#
"envoy.grpc_credentials.file_based_metadata": "//source/extensions/grpc_credentials/file_based_metadata:config",
"envoy.grpc_credentials.aws_iam": "//source/extensions/grpc_credentials/aws_iam:config",
#
# WASM
@ -94,11 +94,6 @@ ENVOY_EXTENSIONS = {
"envoy.matching.actions.format_string": "//source/extensions/matching/actions/format_string:config",
#
# StringMatchers
#
"envoy.string_matcher.lua": "//source/extensions/string_matcher/lua:config",
#
# HTTP filters
#
@ -109,7 +104,6 @@ ENVOY_EXTENSIONS = {
"envoy.filters.http.aws_lambda": "//source/extensions/filters/http/aws_lambda:config",
"envoy.filters.http.aws_request_signing": "//source/extensions/filters/http/aws_request_signing:config",
"envoy.filters.http.bandwidth_limit": "//source/extensions/filters/http/bandwidth_limit:config",
"envoy.filters.http.basic_auth": "//source/extensions/filters/http/basic_auth:config",
"envoy.filters.http.buffer": "//source/extensions/filters/http/buffer:config",
"envoy.filters.http.cache": "//source/extensions/filters/http/cache:config",
"envoy.filters.http.cdn_loop": "//source/extensions/filters/http/cdn_loop:config",
@ -117,7 +111,6 @@ ENVOY_EXTENSIONS = {
"envoy.filters.http.cors": "//source/extensions/filters/http/cors:config",
"envoy.filters.http.composite": "//source/extensions/filters/http/composite:config",
"envoy.filters.http.connect_grpc_bridge": "//source/extensions/filters/http/connect_grpc_bridge:config",
"envoy.filters.http.credential_injector": "//source/extensions/filters/http/credential_injector:config",
"envoy.filters.http.csrf": "//source/extensions/filters/http/csrf:config",
"envoy.filters.http.decompressor": "//source/extensions/filters/http/decompressor:config",
"envoy.filters.http.dynamic_forward_proxy": "//source/extensions/filters/http/dynamic_forward_proxy:config",
@ -135,7 +128,6 @@ ENVOY_EXTENSIONS = {
"envoy.filters.http.header_mutation": "//source/extensions/filters/http/header_mutation:config",
"envoy.filters.http.health_check": "//source/extensions/filters/http/health_check:config",
"envoy.filters.http.ip_tagging": "//source/extensions/filters/http/ip_tagging:config",
"envoy.filters.http.json_to_metadata": "//source/extensions/filters/http/json_to_metadata:config",
"envoy.filters.http.jwt_authn": "//source/extensions/filters/http/jwt_authn:config",
# Disabled by default
"envoy.filters.http.kill_request": "//source/extensions/filters/http/kill_request:kill_request_config",
@ -145,10 +137,8 @@ ENVOY_EXTENSIONS = {
"envoy.filters.http.on_demand": "//source/extensions/filters/http/on_demand:config",
"envoy.filters.http.original_src": "//source/extensions/filters/http/original_src:config",
"envoy.filters.http.ratelimit": "//source/extensions/filters/http/ratelimit:config",
"envoy.filters.http.rate_limit_quota": "//source/extensions/filters/http/rate_limit_quota:config",
"envoy.filters.http.rbac": "//source/extensions/filters/http/rbac:config",
"envoy.filters.http.router": "//source/extensions/filters/http/router:config",
"envoy.filters.http.set_filter_state": "//source/extensions/filters/http/set_filter_state:config",
"envoy.filters.http.set_metadata": "//source/extensions/filters/http/set_metadata:config",
"envoy.filters.http.tap": "//source/extensions/filters/http/tap:config",
"envoy.filters.http.wasm": "//source/extensions/filters/http/wasm:config",
@ -185,7 +175,6 @@ ENVOY_EXTENSIONS = {
"envoy.filters.network.redis_proxy": "//source/extensions/filters/network/redis_proxy:config",
"envoy.filters.network.tcp_proxy": "//source/extensions/filters/network/tcp_proxy:config",
"envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:config",
"envoy.filters.network.set_filter_state": "//source/extensions/filters/network/set_filter_state:config",
"envoy.filters.network.sni_cluster": "//source/extensions/filters/network/sni_cluster:config",
"envoy.filters.network.sni_dynamic_forward_proxy": "//source/extensions/filters/network/sni_dynamic_forward_proxy:config",
"envoy.filters.network.wasm": "//source/extensions/filters/network/wasm:config",
@ -202,10 +191,8 @@ ENVOY_EXTENSIONS = {
# Resource monitors
#
"envoy.resource_monitors.cpu_utilization": "//source/extensions/resource_monitors/cpu_utilization:config",
"envoy.resource_monitors.fixed_heap": "//source/extensions/resource_monitors/fixed_heap:config",
"envoy.resource_monitors.injected_resource": "//source/extensions/resource_monitors/injected_resource:config",
"envoy.resource_monitors.downstream_connections": "//source/extensions/resource_monitors/downstream_connections:config",
#
# Stat sinks
@ -231,26 +218,14 @@ ENVOY_EXTENSIONS = {
# Tracers
#
"envoy.tracers.dynamic_ot": "//source/extensions/tracers/dynamic_ot:config",
"envoy.tracers.datadog": "//source/extensions/tracers/datadog:config",
"envoy.tracers.zipkin": "//source/extensions/tracers/zipkin:config",
"envoy.tracers.opencensus": "//source/extensions/tracers/opencensus:config",
"envoy.tracers.xray": "//source/extensions/tracers/xray:config",
"envoy.tracers.skywalking": "//source/extensions/tracers/skywalking:config",
"envoy.tracers.opentelemetry": "//source/extensions/tracers/opentelemetry:config",
#
# OpenTelemetry Resource Detectors
#
"envoy.tracers.opentelemetry.resource_detectors.environment": "//source/extensions/tracers/opentelemetry/resource_detectors/environment:config",
"envoy.tracers.opentelemetry.resource_detectors.dynatrace": "//source/extensions/tracers/opentelemetry/resource_detectors/dynatrace:config",
#
# OpenTelemetry tracer samplers
#
"envoy.tracers.opentelemetry.samplers.always_on": "//source/extensions/tracers/opentelemetry/samplers/always_on:config",
"envoy.tracers.opentelemetry.samplers.dynatrace": "//source/extensions/tracers/opentelemetry/samplers/dynatrace:config",
#
# Transport sockets
#
@ -261,7 +236,6 @@ ENVOY_EXTENSIONS = {
"envoy.transport_sockets.tap": "//source/extensions/transport_sockets/tap:config",
"envoy.transport_sockets.starttls": "//source/extensions/transport_sockets/starttls:config",
"envoy.transport_sockets.tcp_stats": "//source/extensions/transport_sockets/tcp_stats:config",
"envoy.transport_sockets.tls": "//source/extensions/transport_sockets/tls:config",
"envoy.transport_sockets.internal_upstream": "//source/extensions/transport_sockets/internal_upstream:config",
#
@ -312,6 +286,7 @@ ENVOY_EXTENSIONS = {
"envoy.wasm.runtime.null": "//source/extensions/wasm_runtime/null:config",
"envoy.wasm.runtime.v8": "//source/extensions/wasm_runtime/v8:config",
"envoy.wasm.runtime.wamr": "//source/extensions/wasm_runtime/wamr:config",
"envoy.wasm.runtime.wavm": "//source/extensions/wasm_runtime/wavm:config",
"envoy.wasm.runtime.wasmtime": "//source/extensions/wasm_runtime/wasmtime:config",
#
@ -357,7 +332,7 @@ ENVOY_EXTENSIONS = {
# QUIC extensions
#
"envoy.quic.deterministic_connection_id_generator": "//source/extensions/quic/connection_id_generator/deterministic:envoy_deterministic_connection_id_generator_config",
"envoy.quic.deterministic_connection_id_generator": "//source/extensions/quic/connection_id_generator:envoy_deterministic_connection_id_generator_config",
"envoy.quic.crypto_stream.server.quiche": "//source/extensions/quic/crypto_stream:envoy_quic_default_crypto_server_stream",
"envoy.quic.proof_source.filter_chain": "//source/extensions/quic/proof_source:envoy_quic_default_proof_source",
@ -373,7 +348,6 @@ ENVOY_EXTENSIONS = {
"envoy.formatter.metadata": "//source/extensions/formatter/metadata:config",
"envoy.formatter.req_without_query": "//source/extensions/formatter/req_without_query:config",
"envoy.formatter.cel": "//source/extensions/formatter/cel:config",
#
# Key value store
@ -417,13 +391,6 @@ ENVOY_EXTENSIONS = {
"envoy.load_balancing_policies.maglev": "//source/extensions/load_balancing_policies/maglev:config",
"envoy.load_balancing_policies.ring_hash": "//source/extensions/load_balancing_policies/ring_hash:config",
"envoy.load_balancing_policies.subset": "//source/extensions/load_balancing_policies/subset:config",
"envoy.load_balancing_policies.cluster_provided": "//source/extensions/load_balancing_policies/cluster_provided:config",
"envoy.load_balancing_policies.override_host": "//source/extensions/load_balancing_policies/override_host:config",
#
# HTTP Early Header Mutation
#
"envoy.http.early_header_mutation.header_mutation": "//source/extensions/http/early_header_mutation/header_mutation:config",
#
# Config Subscription
@ -437,19 +404,6 @@ ENVOY_EXTENSIONS = {
"envoy.config_subscription.aggregated_grpc_collection": "//source/extensions/config_subscription/grpc:grpc_collection_subscription_lib",
"envoy.config_subscription.aggregated_delta_grpc_collection": "//source/extensions/config_subscription/grpc:grpc_collection_subscription_lib",
"envoy.config_subscription.ads_collection": "//source/extensions/config_subscription/grpc:grpc_collection_subscription_lib",
#
# cluster specifier plugin
#
"envoy.router.cluster_specifier_plugin.lua": "//source/extensions/router/cluster_specifiers/lua:config",
#
# Injected credentials
#
"envoy.http.injected_credentials.generic": "//source/extensions/http/injected_credentials/generic:config",
"envoy.http.injected_credentials.oauth2": "//source/extensions/http/injected_credentials/oauth2:config",
}
ENVOY_CONTRIB_EXTENSIONS = {
@ -498,7 +452,7 @@ ENVOY_CONTRIB_EXTENSIONS = {
# Connection Balance extensions
#
"envoy.network.connection_balance.dlb": "//contrib/dlb/source:connection_balancer",
"envoy.network.connection_balance.dlb": "//contrib/network/connection_balance/dlb/source:connection_balancer",
}

130
bazel/repositories.bzl Normal file
View File

@ -0,0 +1,130 @@
# Copyright 2017 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("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
#
# To update these...
# 1) find the ISTIO_API SHA you want in git
# 2) wget https://github.com/istio/api/archive/$ISTIO_API_SHA.tar.gz && sha256sum $ISTIO_API_SHA.tar.gz
#
ISTIO_API = "75bb24b620144218d26b92afedbb428e4d84e506"
ISTIO_API_SHA256 = "c72602c38f7ab10e430618e4ce82fee143f4446d468863ba153ea897bdff2298"
def istioapi_repositories(bind = True):
BUILD = """
# 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.
#
################################################################################
#
proto_library(
name = "authentication_policy_config_proto_lib",
srcs = glob(
["envoy/config/filter/http/authn/v2alpha1/*.proto",
"authentication/v1alpha1/*.proto",
"common/v1alpha1/*.proto",
],
),
visibility = ["//visibility:public"],
deps = [
"@com_google_googleapis//google/api:field_behavior_proto",
],
)
cc_proto_library(
name = "authentication_policy_config_cc_proto",
visibility = ["//visibility:public"],
deps = [
":authentication_policy_config_proto_lib",
],
)
proto_library(
name = "alpn_filter_config_proto_lib",
srcs = glob(
["envoy/config/filter/http/alpn/v2alpha1/*.proto", ],
),
visibility = ["//visibility:public"],
)
cc_proto_library(
name = "alpn_filter_config_cc_proto",
visibility = ["//visibility:public"],
deps = [
":alpn_filter_config_proto_lib",
],
)
proto_library(
name = "tcp_cluster_rewrite_config_proto_lib",
srcs = glob(
["envoy/config/filter/network/tcp_cluster_rewrite/v2alpha1/*.proto", ],
),
visibility = ["//visibility:public"],
)
cc_proto_library(
name = "tcp_cluster_rewrite_config_cc_proto",
visibility = ["//visibility:public"],
deps = [
":tcp_cluster_rewrite_config_proto_lib",
],
)
"""
http_archive(
name = "istioapi_git",
build_file_content = BUILD,
strip_prefix = "api-" + ISTIO_API,
url = "https://github.com/istio/api/archive/" + ISTIO_API + ".tar.gz",
sha256 = ISTIO_API_SHA256,
)
if bind:
native.bind(
name = "authentication_policy_config_cc_proto",
actual = "@istioapi_git//:authentication_policy_config_cc_proto",
)
native.bind(
name = "alpn_filter_config_cc_proto",
actual = "@istioapi_git//:alpn_filter_config_cc_proto",
)
native.bind(
name = "tcp_cluster_rewrite_config_cc_proto",
actual = "@istioapi_git//:tcp_cluster_rewrite_config_cc_proto",
)
def istioapi_dependencies():
istioapi_repositories()
def docker_dependencies():
http_archive(
name = "io_bazel_rules_docker",
sha256 = "b1e80761a8a8243d03ebca8845e9cc1ba6c82ce7c5179ce2b295cd36f7e394bf",
urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.25.0/rules_docker-v0.25.0.tar.gz"],
)

36
bazel/wasm.bzl Normal file
View File

@ -0,0 +1,36 @@
# Copyright 2020 Google Inc. 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("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")
def wasm_dependencies():
FLAT_BUFFERS_SHA = "a83caf5910644ba1c421c002ef68e42f21c15f9f"
http_archive(
name = "com_github_google_flatbuffers",
sha256 = "b8efbc25721e76780752bad775a97c3f77a0250271e2db37fc747b20e8b0f24a",
strip_prefix = "flatbuffers-" + FLAT_BUFFERS_SHA,
url = "https://github.com/google/flatbuffers/archive/" + FLAT_BUFFERS_SHA + ".tar.gz",
)
http_file(
name = "com_github_nlohmann_json_single_header",
sha256 = "3b5d2b8f8282b80557091514d8ab97e27f9574336c804ee666fda673a9b59926",
urls = [
"https://github.com/nlohmann/json/releases/download/v3.7.3/json.hpp",
],
)

View File

@ -1 +1 @@
d46067e1a8ba3db4abe2635af5807f00ba1981e6
bdb38aa251ecadf811809709d381eb2d8f62d9cf

View File

@ -19,8 +19,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
FINDFILES=find . \( -path ./common-protos -o -path ./.git -o -path ./out -o -path ./.github -o -path ./licenses -o -path ./vendor $(if $(strip ${FINDFILES_IGNORE}), -o ${FINDFILES_IGNORE}) \) -prune -o -type f
FINDFILES=find . \( -path ./common-protos -o -path ./.git -o -path ./out -o -path ./.github -o -path ./licenses -o -path ./vendor \) -prune -o -type f
XARGS = xargs -0 -r
lint-dockerfiles:
@ -50,7 +49,7 @@ lint-python:
@${FINDFILES} -name '*.py' \( ! \( -name '*_pb2.py' \) \) -print0 | ${XARGS} autopep8 --max-line-length 160 --exit-code -d
lint-markdown:
@${FINDFILES} -name '*.md' -not -path './manifests/addons/dashboards/*' -print0 | ${XARGS} mdl --ignore-front-matter --style common/config/mdl.rb
@${FINDFILES} -name '*.md' -print0 | ${XARGS} mdl --ignore-front-matter --style common/config/mdl.rb
lint-links:
@${FINDFILES} -name '*.md' -print0 | ${XARGS} awesome_bot --skip-save-results --allow_ssl --allow-timeout --allow-dupe --allow-redirect --white-list ${MARKDOWN_LINT_ALLOWLIST}
@ -92,7 +91,7 @@ mirror-licenses: mod-download-go
@license-lint --mirror
TMP := $(shell mktemp -d -u)
UPDATE_BRANCH ?= "master"
UPDATE_BRANCH ?= "release-1.19"
BUILD_TOOLS_ORG ?= "istio"
@ -101,16 +100,12 @@ update-common:
@git clone -q --depth 1 --single-branch --branch $(UPDATE_BRANCH) https://github.com/$(BUILD_TOOLS_ORG)/common-files $(TMP)/common-files
@cd $(TMP)/common-files ; git rev-parse HEAD >files/common/.commonfiles.sha
@rm -fr common
# istio/community has its own CONTRIBUTING.md file.
@CONTRIB_OVERRIDE=$(shell grep -l "istio/community/blob/master/CONTRIBUTING.md" CONTRIBUTING.md)
@if [ "$(CONTRIB_OVERRIDE)" != "CONTRIBUTING.md" ]; then\
rm $(TMP)/common-files/files/CONTRIBUTING.md;\
fi
@cp -a $(TMP)/common-files/files/* $(TMP)/common-files/files/.devcontainer $(TMP)/common-files/files/.gitattributes $(shell pwd)
@rm -fr $(TMP)/common-files
@if [ "$(AUTOMATOR_REPO)" == "proxy" ]; then\
sed -i -e 's/build-tools:/build-tools-proxy:/g' .devcontainer/devcontainer.json;\
fi
@$(or $(COMMONFILES_POSTPROCESS), true)
check-clean-repo:

59
common/config/.golangci-format.yml vendored Normal file
View File

@ -0,0 +1,59 @@
# WARNING: DO NOT EDIT, THIS FILE IS PROBABLY A COPY
#
# The original version of this file is located in the https://github.com/istio/common-files repo.
# If you're looking at this file in a different repo and want to make a change, please go to the
# common-files repo, make the change there and check it in. Then come back to this repo and run
# "make update-common".
service:
# When updating this, also update the version stored in docker/build-tools/Dockerfile in the istio/tools repo.
golangci-lint-version: 1.49.x # use the fixed version to not introduce new linters unexpectedly
run:
# timeout for analysis, e.g. 30s, 5m, default is 1m
deadline: 20m
build-tags:
- integ
- integfuzz
# which dirs to skip: they won't be analyzed;
# can use regexp here: generated.*, regexp is applied on full path;
# default value is empty list, but next dirs are always skipped independently
# from this option's value:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
skip-dirs:
- genfiles$
- vendor$
# which files to skip: they will be analyzed, but issues from them
# won't be reported. Default value is empty list, but there is
# no need to include all autogenerated files, we confidently recognize
# autogenerated files. If it's not please let us know.
skip-files:
- ".*\\.pb\\.go"
- ".*\\.gen\\.go"
linters:
disable-all: true
enable:
- goimports
- gofumpt
- gci
fast: false
linters-settings:
gci:
sections:
- standard # Captures all standard packages if they do not match another section.
- default # Contains all imports that could not be matched to another section type.
- prefix(istio.io/) # Groups all imports with the specified Prefix.
goimports:
# put imports beginning with prefix after 3rd-party packages;
# it's a comma-separated list of prefixes
local-prefixes: istio.io/
issues:
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
max-per-linter: 0
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
max-same-issues: 0

View File

@ -1,221 +1,274 @@
version: "2"
# WARNING: DO NOT EDIT, THIS FILE IS PROBABLY A COPY
#
# The original version of this file is located in the https://github.com/istio/common-files repo.
# If you're looking at this file in a different repo and want to make a change, please go to the
# common-files repo, make the change there and check it in. Then come back to this repo and run
# "make update-common".
service:
# When updating this, also update the version stored in docker/build-tools/Dockerfile in the istio/tools repo.
golangci-lint-version: 1.53.x # use the fixed version to not introduce new linters unexpectedly
run:
# timeout for analysis, e.g. 30s, 5m, default is 1m
deadline: 20m
build-tags:
- integ
- integfuzz
# which dirs to skip: they won't be analyzed;
# can use regexp here: generated.*, regexp is applied on full path;
# default value is empty list, but next dirs are always skipped independently
# from this option's value:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
skip-dirs:
- genfiles$
- vendor$
# which files to skip: they will be analyzed, but issues from them
# won't be reported. Default value is empty list, but there is
# no need to include all autogenerated files, we confidently recognize
# autogenerated files. If it's not please let us know.
skip-files:
- ".*\\.pb\\.go"
- ".*\\.gen\\.go"
linters:
default: none
disable-all: true
enable:
- copyloopvar
- depguard
- errcheck
- exportloopref
- depguard
- gocritic
- gosec
- gofumpt
- goimports
- revive
- gosimple
- govet
- ineffassign
- lll
- misspell
- revive
- staticcheck
- stylecheck
- typecheck
- unconvert
- unparam
- unused
settings:
depguard:
rules:
DenyGogoProtobuf:
files:
- $all
deny:
- pkg: github.com/gogo/protobuf
desc: gogo/protobuf is deprecated, use golang/protobuf
errcheck:
check-type-assertions: false
check-blank: false
gocritic:
disable-all: true
enabled-checks:
- appendCombine
- argOrder
- assignOp
- badCond
- boolExprSimplify
- builtinShadow
- captLocal
- caseOrder
- codegenComment
- commentedOutCode
- commentedOutImport
- defaultCaseOrder
- deprecatedComment
- docStub
- dupArg
- dupBranchBody
- dupCase
- dupSubExpr
- elseif
- emptyFallthrough
- equalFold
- flagDeref
- flagName
- hexLiteral
- indexAlloc
- initClause
- methodExprCall
- nilValReturn
- octalLiteral
- offBy1
- rangeExprCopy
- regexpMust
- sloppyLen
- stringXbytes
- switchTrue
- typeAssertChain
- typeSwitchVar
- typeUnparen
- underef
- unlambda
- unnecessaryBlock
- unslice
- valSwap
- weakCond
gosec:
includes:
- G401
- G402
- G404
govet:
disable:
- shadow
lll:
line-length: 160
tab-width: 1
misspell:
locale: US
ignore-rules:
- cancelled
revive:
confidence: 0
severity: warning
rules:
- name: blank-imports
- name: context-keys-type
- name: time-naming
- name: var-declaration
- name: unexported-return
- name: errorf
- name: context-as-argument
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
- name: increment-decrement
- name: var-naming
- name: package-comments
- name: range
- name: receiver-naming
- name: indent-error-flow
- name: superfluous-else
- name: modifies-parameter
- name: unreachable-code
- name: struct-tag
- name: constant-logical-expr
- name: bool-literal-in-expr
- name: redefines-builtin-id
- name: imports-blocklist
- name: range-val-in-closure
- name: range-val-address
- name: waitgroup-by-value
- name: atomic
- name: call-to-gc
- name: duplicated-imports
- name: string-of-int
- name: defer
arguments:
- - call-chain
- name: unconditional-recursion
- name: identical-branches
unparam:
check-exported: false
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- errcheck
- maligned
path: _test\.go$|tests/|samples/
- path: _test\.go$
text: 'dot-imports: should not use dot imports'
- linters:
- staticcheck
text: 'SA1019: package github.com/golang/protobuf/jsonpb'
- linters:
- staticcheck
text: 'SA1019: "github.com/golang/protobuf/jsonpb"'
- linters:
- staticcheck
text: 'SA1019: grpc.Dial is deprecated: use NewClient instead'
- linters:
- staticcheck
text: 'SA1019: grpc.DialContext is deprecated: use NewClient instead'
- linters:
- staticcheck
text: 'SA1019: grpc.WithBlock is deprecated'
- linters:
- staticcheck
text: 'SA1019: grpc.FailOnNonTempDialError'
- linters:
- staticcheck
text: 'SA1019: grpc.WithReturnConnectionError'
- path: (.+)\.go$
text: composite literal uses unkeyed fields
# TODO: remove following rule in the future
- linters:
- staticcheck
text: 'QF'
- linters:
- staticcheck
text: 'ST1005'
- linters:
- staticcheck
text: 'S1007'
paths:
- .*\.pb\.go
- .*\.gen\.go
- genfiles$
- vendor$
- third_party$
- builtin$
- examples$
issues:
max-issues-per-linter: 0
max-same-issues: 0
formatters:
enable:
- gci
- gofumpt
- goimports
settings:
gci:
sections:
- standard
- default
- prefix(istio.io/)
goimports:
local-prefixes:
- istio.io/
exclusions:
generated: lax
paths:
- .*\.pb\.go
- .*\.gen\.go
- genfiles$
- vendor$
- third_party$
- builtin$
- examples$
- gosec
fast: false
linters-settings:
errcheck:
# report about not checking of errors in type assertions: `a := b.(MyStruct)`;
# default is false: such cases aren't reported by default.
check-type-assertions: false
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
# default is false: such cases aren't reported by default.
check-blank: false
govet:
# report about shadowed variables
check-shadowing: false
goimports:
# put imports beginning with prefix after 3rd-party packages;
# it's a comma-separated list of prefixes
local-prefixes: istio.io/
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
misspell:
# Correct spellings using locale preferences for US or UK.
# Default is to use a neutral variety of English.
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
locale: US
ignore-words:
- cancelled
lll:
# max line length, lines longer will be reported. Default is 120.
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
line-length: 160
# tab width in spaces. Default to 1.
tab-width: 1
revive:
ignore-generated-header: false
severity: "warning"
confidence: 0.0
error-code: 2
warning-code: 1
rules:
- name: blank-imports
- name: context-keys-type
- name: time-naming
- name: var-declaration
- name: unexported-return
- name: errorf
- name: context-as-argument
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
- name: increment-decrement
- name: var-naming
- name: package-comments
- name: range
- name: receiver-naming
- name: indent-error-flow
- name: superfluous-else
- name: modifies-parameter
- name: unreachable-code
- name: struct-tag
- name: constant-logical-expr
- name: bool-literal-in-expr
- name: redefines-builtin-id
- name: imports-blacklist
- name: range-val-in-closure
- name: range-val-address
- name: waitgroup-by-value
- name: atomic
- name: call-to-gc
- name: duplicated-imports
- name: string-of-int
- name: defer
arguments:
- - "call-chain"
- name: unconditional-recursion
- name: identical-branches
# the following rules can be enabled in the future
# - name: empty-lines
# - name: confusing-results
# - name: empty-block
# - name: get-return
# - name: confusing-naming
# - name: unexported-naming
# - name: early-return
# - name: unused-parameter
# - name: unnecessary-stmt
# - name: deep-exit
# - name: import-shadowing
# - name: modifies-value-receiver
# - name: unused-receiver
# - name: bare-return
# - name: flag-parameter
# - name: unhandled-error
# - name: if-return
unused:
# treat code as a program (not a library) and report unused exported identifiers; default is false.
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
unparam:
# call graph construction algorithm (cha, rta). In general, use cha for libraries,
# and rta for programs with main packages. Default is cha.
algo: cha
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
gci:
sections:
- standard # Captures all standard packages if they do not match another section.
- default # Contains all imports that could not be matched to another section type.
- prefix(istio.io/) # Groups all imports with the specified Prefix.
gocritic:
enabled-checks:
- appendCombine
- argOrder
- assignOp
- badCond
- boolExprSimplify
- builtinShadow
- captLocal
- caseOrder
- codegenComment
- commentedOutCode
- commentedOutImport
- defaultCaseOrder
- deprecatedComment
- docStub
- dupArg
- dupBranchBody
- dupCase
- dupSubExpr
- elseif
- emptyFallthrough
- equalFold
- flagDeref
- flagName
- hexLiteral
- indexAlloc
- initClause
- methodExprCall
- nilValReturn
- octalLiteral
- offBy1
- rangeExprCopy
- regexpMust
- sloppyLen
- stringXbytes
- switchTrue
- typeAssertChain
- typeSwitchVar
- typeUnparen
- underef
- unlambda
- unnecessaryBlock
- unslice
- valSwap
- weakCond
# Unused
# - yodaStyleExpr
# - appendAssign
# - commentFormatting
# - emptyStringTest
# - exitAfterDefer
# - ifElseChain
# - hugeParam
# - importShadow
# - nestingReduce
# - paramTypeCombine
# - ptrToRefParam
# - rangeValCopy
# - singleCaseSwitch
# - sloppyReassign
# - unlabelStmt
# - unnamedResult
# - wrapperFunc
depguard:
rules:
DenyGogoProtobuf:
files:
- $all
deny:
- pkg: github.com/gogo/protobuf
desc: "gogo/protobuf is deprecated, use golang/protobuf"
gosec:
includes:
- G401
- G402
- G404
issues:
# List of regexps of issue texts to exclude, empty list by default.
# But independently from this option we use default exclude patterns,
# it can be disabled by `exclude-use-default: false`. To list all
# excluded by default patterns execute `golangci-lint run --help`
exclude:
- composite literal uses unkeyed fields
exclude-rules:
# Exclude some linters from running on test files.
- path: _test\.go$|^tests/|^samples/
linters:
- errcheck
- maligned
# We need to use the deprecated module since the jsonpb replacement is not backwards compatible.
- linters:
- staticcheck
text: "SA1019: package github.com/golang/protobuf/jsonpb"
- linters:
- staticcheck
text: 'SA1019: "github.com/golang/protobuf/jsonpb"'
# Independently from option `exclude` we use default exclude patterns,
# it can be disabled by this option. To list all
# excluded by default patterns execute `golangci-lint run --help`.
# Default value for this option is true.
exclude-use-default: true
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
max-per-linter: 0
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
max-same-issues: 0

View File

@ -13,5 +13,3 @@ trustedRegistries:
- gcr.io
- docker.io
- quay.io
- "*.pkg.dev"
- "cgr.dev"

View File

@ -125,21 +125,4 @@ allowlisted_modules:
# Simplified BSD (BSD-2-Clause): https://github.com/russross/blackfriday/blob/master/LICENSE.txt
- github.com/russross/blackfriday
- github.com/russross/blackfriday/v2
# W3C Test Suite License, W3C 3-clause BSD License
# gonum uses this for its some of its test files
# gonum.org/v1/gonum/graph/formats/rdf/testdata/LICENSE.md
- gonum.org/v1/gonum
# BSD 3-clause: https://github.com/go-inf/inf/blob/v0.9.1/LICENSE
- gopkg.in/inf.v0
# BSD 3-clause: https://github.com/go-git/gcfg/blob/main/LICENSE
- github.com/go-git/gcfg
# Apache 2.0
- github.com/aws/smithy-go
# Simplified BSD License: https://github.com/gomarkdown/markdown/blob/master/LICENSE.txt
- github.com/gomarkdown/markdown
- github.com/russross/blackfriday/v2

View File

@ -24,7 +24,6 @@ function write_patch_file() {
git diff > "${PATCH_OUT}"
[ -n "${JOB_NAME}" ] && [ -n "${BUILD_ID}" ]
# shellcheck disable=SC2319
IN_PROW="$?"
# Don't persist large diffs (30M+) on CI

View File

@ -21,4 +21,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
golangci-lint run --fix -c ./common/config/.golangci.yml
golangci-lint run --fix -c ./common/config/.golangci-format.yml

View File

@ -32,10 +32,7 @@ set -x
####################################################################
# DEFAULT_KIND_IMAGE is used to set the Kubernetes version for KinD unless overridden in params to setup_kind_cluster(s)
DEFAULT_KIND_IMAGE="gcr.io/istio-testing/kind-node:v1.33.1"
# the default kind cluster should be ipv4 if not otherwise specified
KIND_IP_FAMILY="${KIND_IP_FAMILY:-ipv4}"
DEFAULT_KIND_IMAGE="gcr.io/istio-testing/kind-node:v1.27.3"
# COMMON_SCRIPTS contains the directory this file is in.
COMMON_SCRIPTS=$(dirname "${BASH_SOURCE:-$0}")
@ -133,7 +130,7 @@ function cleanup_kind_cluster() {
# check_default_cluster_yaml checks the presence of default cluster YAML
# It returns 1 if it is not present
function check_default_cluster_yaml() {
if [[ -z "${DEFAULT_CLUSTER_YAML:-}" ]]; then
if [[ -z "${DEFAULT_CLUSTER_YAML}" ]]; then
echo 'DEFAULT_CLUSTER_YAML file must be specified. Exiting...'
return 1
fi
@ -147,15 +144,15 @@ function setup_kind_cluster_retry() {
# 1. NAME: Name of the Kind cluster (optional)
# 2. IMAGE: Node image used by KinD (optional)
# 3. CONFIG: KinD cluster configuration YAML file. If not specified then DEFAULT_CLUSTER_YAML is used
# 4. NOMETALBINSTALL: Dont install metalb if set.
# 4. NOMETALBINSTALL: Dont install matllb if set.
# This function returns 0 when everything goes well, or 1 otherwise
# If Kind cluster was already created then it would be cleaned up in case of errors
function setup_kind_cluster() {
local NAME="${1:-istio-testing}"
local IMAGE="${2:-"${DEFAULT_KIND_IMAGE}"}"
local CONFIG="${3:-}"
local NOMETALBINSTALL="${4:-}"
local CLEANUP="${5:-true}"
NAME="${1:-istio-testing}"
IMAGE="${2:-"${DEFAULT_KIND_IMAGE}"}"
CONFIG="${3:-}"
NOMETALBINSTALL="${4:-}"
CLEANUP="${5:-true}"
check_default_cluster_yaml
@ -175,56 +172,27 @@ function setup_kind_cluster() {
if [[ -z "${CONFIG}" ]]; then
# Kubernetes 1.15+
CONFIG=${DEFAULT_CLUSTER_YAML}
fi
KIND_WAIT_FLAG="--wait=180s"
KIND_DISABLE_CNI="false"
if [[ -n "${KUBERNETES_CNI:-}" ]]; then
unset KIND_WAIT_FLAG
KIND_DISABLE_CNI="true"
# Configure the cluster IP Family only for default configs
if [ "${IP_FAMILY}" != "ipv4" ]; then
grep "ipFamily: ${IP_FAMILY}" "${CONFIG}" || \
cat <<EOF >> "${CONFIG}"
networking:
ipFamily: ${IP_FAMILY}
EOF
fi
fi
# Create KinD cluster
if ! (yq eval "${CONFIG}" --expression ".networking.disableDefaultCNI = ${KIND_DISABLE_CNI}" \
--expression ".networking.ipFamily = \"${KIND_IP_FAMILY}\"" | \
kind create cluster --name="${NAME}" -v4 --retain --image "${IMAGE}" ${KIND_WAIT_FLAG:+"$KIND_WAIT_FLAG"} --config -); then
if ! (kind create cluster --name="${NAME}" --config "${CONFIG}" -v4 --retain --image "${IMAGE}" --wait=180s); then
echo "Could not setup KinD environment. Something wrong with KinD setup. Exporting logs."
return 9
# kubectl config set clusters.kind-istio-testing.server https://istio-testing-control-plane:6443
fi
if [[ -n "${DEVCONTAINER:-}" ]]; then
# identify our docker container id using proc and regex
containerid=$(grep 'resolv.conf' /proc/self/mountinfo | sed 's/.*\/docker\/containers\/\([0-9a-f]*\).*/\1/')
docker network connect kind "$containerid"
kind export kubeconfig --name="${NAME}" --internal
fi
# Workaround kind issue causing taints to not be removed in 1.24
kubectl taint nodes "${NAME}"-control-plane node-role.kubernetes.io/control-plane- 2>/dev/null || true
# Determine what CNI to install
case "${KUBERNETES_CNI:-}" in
"calico")
echo "Installing Calico CNI"
install_calico "" "$(dirname "$CONFIG")"
;;
"")
# perfectly fine, we accepted the default KinD CNI
;;
*)
# we don't know what to do but we've got no CNI, return non-zero
echo "${KUBERNETES_CNI} is not recognized. Supported options are \"calico\" or do not set the variable to use default."
return 1
;;
esac
kubectl taint nodes "${NAME}"-control-plane node-role.kubernetes.io/control-plane- || true
# If metrics server configuration directory is specified then deploy in
# the cluster just created
if [[ -n ${METRICS_SERVER_CONFIG_DIR:-} ]]; then
if [[ -n ${METRICS_SERVER_CONFIG_DIR} ]]; then
retry kubectl apply -f "${METRICS_SERVER_CONFIG_DIR}"
fi
@ -239,7 +207,7 @@ function setup_kind_cluster() {
# https://github.com/coredns/coredns/issues/2494#issuecomment-457215452
# CoreDNS should handle those domains and answer with NXDOMAIN instead of SERVFAIL
# otherwise pods stops trying to resolve the domain.
if [ "${KIND_IP_FAMILY}" = "ipv6" ] || [ "${KIND_IP_FAMILY}" = "dual" ]; then
if [ "${IP_FAMILY}" = "ipv6" ] || [ "${IP_FAMILY}" = "dual" ]; then
# Get the current config
original_coredns=$(kubectl get -oyaml -n=kube-system configmap/coredns)
echo "Original CoreDNS config:"
@ -276,14 +244,14 @@ function cleanup_kind_clusters() {
# setup_kind_clusters sets up a given number of kind clusters with given topology
# as specified in cluster topology configuration file.
# 1. IMAGE = docker image used as node by KinD
# 2. KIND_IP_FAMILY = either ipv4 or ipv6 or dual
# 2. IP_FAMILY = either ipv4 or ipv6
#
# NOTE: Please call load_cluster_topology before calling this method as it expects
# cluster topology information to be loaded in advance
function setup_kind_clusters() {
IMAGE="${1:-"${DEFAULT_KIND_IMAGE}"}"
KUBECONFIG_DIR="${ARTIFACTS:-$(mktemp -d)}/kubeconfig"
KIND_IP_FAMILY="${2:-ipv4}"
IP_FAMILY="${2:-ipv4}"
check_default_cluster_yaml
@ -394,21 +362,10 @@ function connect_kind_clusters() {
fi
}
function install_calico {
local KUBECONFIG="${1}"
local CONFIG_DIR="${2}"
echo "Setting up ambient cluster, Calico CNI will be used."
kubectl --kubeconfig="$KUBECONFIG" apply -f "${CONFIG_DIR}"/calico.yaml
kubectl --kubeconfig="$KUBECONFIG" wait --for condition=ready -n kube-system pod -l k8s-app=calico-node --timeout 90s
kubectl --kubeconfig="$KUBECONFIG" wait --for condition=ready -n kube-system pod -l k8s-app=calico-kube-controllers --timeout 90s
}
function install_metallb() {
KUBECONFIG="${1}"
kubectl --kubeconfig="$KUBECONFIG" apply -f "${COMMON_SCRIPTS}/metallb-native.yaml"
kubectl --kubeconfig="$KUBECONFIG" wait -n metallb-system pod --timeout=120s -l app=metallb --for=condition=Ready
kubectl apply --kubeconfig="$KUBECONFIG" -f "${COMMON_SCRIPTS}/metallb.yaml"
kubectl create --kubeconfig="$KUBECONFIG" secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"
if [ -z "${METALLB_IPS4+x}" ]; then
# Take IPs from the end of the docker kind network subnet to use for MetalLB IPs
@ -439,25 +396,17 @@ function install_metallb() {
done
RANGE="${RANGE%?}]"
echo '
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
echo 'apiVersion: v1
kind: ConfigMap
metadata:
name: default-pool
namespace: metallb-system
spec:
addresses: '"$RANGE"'
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: default-l2
namespace: metallb-system
spec:
ipAddressPools:
- default-pool
' | kubectl apply --kubeconfig="$KUBECONFIG" -f -
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses: '"$RANGE" | kubectl apply --kubeconfig="$KUBECONFIG" -f -
}
function cidr_to_ips() {

View File

@ -21,10 +21,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
GOLANGCILINT_RUN_ARGS=(--output.text.path stdout --output.junit-xml.path "${ARTIFACTS}"/junit-lint.xml)
if [[ "${ARTIFACTS}" != "" ]]; then
golangci-lint run -v -c ./common/config/.golangci.yml "${GOLANGCILINT_RUN_ARGS[@]}"
golangci-lint run -v -c ./common/config/.golangci.yml --out-format colored-line-number,junit-xml:"${ARTIFACTS}"/junit-lint.xml
else
golangci-lint run -v -c ./common/config/.golangci.yml
fi

File diff suppressed because it is too large Load Diff

409
common/scripts/metallb.yaml vendored Normal file
View File

@ -0,0 +1,409 @@
# from https://github.com/metallb/metallb/blob/v0.12/manifests namespace.yaml and metallb.yaml
apiVersion: v1
kind: Namespace
metadata:
name: metallb-system
labels:
app: metallb
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app: metallb
name: controller
namespace: metallb-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app: metallb
name: speaker
namespace: metallb-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app: metallb
name: metallb-system:controller
rules:
- apiGroups:
- ''
resources:
- services
verbs:
- get
- list
- watch
- apiGroups:
- ''
resources:
- services/status
verbs:
- update
- apiGroups:
- ''
resources:
- events
verbs:
- create
- patch
- apiGroups:
- policy
resourceNames:
- controller
resources:
- podsecuritypolicies
verbs:
- use
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app: metallb
name: metallb-system:speaker
rules:
- apiGroups:
- ''
resources:
- services
- endpoints
- nodes
verbs:
- get
- list
- watch
- apiGroups: ["discovery.k8s.io"]
resources:
- endpointslices
verbs:
- get
- list
- watch
- apiGroups:
- ''
resources:
- events
verbs:
- create
- patch
- apiGroups:
- policy
resourceNames:
- speaker
resources:
- podsecuritypolicies
verbs:
- use
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
labels:
app: metallb
name: config-watcher
namespace: metallb-system
rules:
- apiGroups:
- ''
resources:
- configmaps
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
labels:
app: metallb
name: pod-lister
namespace: metallb-system
rules:
- apiGroups:
- ''
resources:
- pods
verbs:
- list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
labels:
app: metallb
name: controller
namespace: metallb-system
rules:
- apiGroups:
- ''
resources:
- secrets
verbs:
- create
- apiGroups:
- ''
resources:
- secrets
resourceNames:
- memberlist
verbs:
- list
- apiGroups:
- apps
resources:
- deployments
resourceNames:
- controller
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
app: metallb
name: metallb-system:controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: metallb-system:controller
subjects:
- kind: ServiceAccount
name: controller
namespace: metallb-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
app: metallb
name: metallb-system:speaker
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: metallb-system:speaker
subjects:
- kind: ServiceAccount
name: speaker
namespace: metallb-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app: metallb
name: config-watcher
namespace: metallb-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: config-watcher
subjects:
- kind: ServiceAccount
name: controller
- kind: ServiceAccount
name: speaker
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app: metallb
name: pod-lister
namespace: metallb-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: pod-lister
subjects:
- kind: ServiceAccount
name: speaker
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app: metallb
name: controller
namespace: metallb-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: controller
subjects:
- kind: ServiceAccount
name: controller
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app: metallb
component: speaker
name: speaker
namespace: metallb-system
spec:
selector:
matchLabels:
app: metallb
component: speaker
template:
metadata:
annotations:
prometheus.io/port: '7472'
prometheus.io/scrape: 'true'
labels:
app: metallb
component: speaker
spec:
containers:
- args:
- --port=7472
- --config=config
- --log-level=info
env:
- name: METALLB_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: METALLB_HOST
valueFrom:
fieldRef:
fieldPath: status.hostIP
- name: METALLB_ML_BIND_ADDR
valueFrom:
fieldRef:
fieldPath: status.podIP
# needed when another software is also using memberlist / port 7946
# when changing this default you also need to update the container ports definition
# and the PodSecurityPolicy hostPorts definition
#- name: METALLB_ML_BIND_PORT
# value: "7946"
- name: METALLB_ML_LABELS
value: "app=metallb,component=speaker"
- name: METALLB_ML_SECRET_KEY
valueFrom:
secretKeyRef:
name: memberlist
key: secretkey
image: gcr.io/istio-testing/metallb/speaker:v0.12.1
name: speaker
ports:
- containerPort: 7472
name: monitoring
- containerPort: 7946
name: memberlist-tcp
- containerPort: 7946
name: memberlist-udp
protocol: UDP
livenessProbe:
httpGet:
path: /metrics
port: monitoring
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
readinessProbe:
httpGet:
path: /metrics
port: monitoring
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_RAW
drop:
- ALL
readOnlyRootFilesystem: true
hostNetwork: true
nodeSelector:
kubernetes.io/os: linux
serviceAccountName: speaker
terminationGracePeriodSeconds: 2
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/master
operator: Exists
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: metallb
component: controller
name: controller
namespace: metallb-system
spec:
revisionHistoryLimit: 3
selector:
matchLabels:
app: metallb
component: controller
template:
metadata:
annotations:
prometheus.io/port: '7472'
prometheus.io/scrape: 'true'
labels:
app: metallb
component: controller
spec:
containers:
- args:
- --port=7472
- --config=config
- --log-level=info
env:
- name: METALLB_ML_SECRET_NAME
value: memberlist
- name: METALLB_DEPLOYMENT
value: controller
image: gcr.io/istio-testing/metallb/controller:v0.12.1
name: controller
ports:
- containerPort: 7472
name: monitoring
livenessProbe:
httpGet:
path: /metrics
port: monitoring
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
readinessProbe:
httpGet:
path: /metrics
port: monitoring
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- all
readOnlyRootFilesystem: true
nodeSelector:
kubernetes.io/os: linux
securityContext:
runAsNonRoot: true
runAsUser: 65534
fsGroup: 65534
serviceAccountName: controller
terminationGracePeriodSeconds: 0

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
# WARNING: DO NOT EDIT, THIS FILE IS PROBABLY A COPY
#
@ -36,7 +36,7 @@ MOUNT_DEST="${MOUNT_DEST:-/work}"
read -ra DOCKER_RUN_OPTIONS <<< "${DOCKER_RUN_OPTIONS:-}"
[[ -t 0 ]] && DOCKER_RUN_OPTIONS+=("-it")
[[ -t 1 ]] && DOCKER_RUN_OPTIONS+=("-it")
[[ ${UID} -ne 0 ]] && DOCKER_RUN_OPTIONS+=(-u "${UID}:${DOCKER_GID}")
# $CONTAINER_OPTIONS becomes an empty arg when quoted, so SC2086 is disabled for the
@ -47,9 +47,7 @@ read -ra DOCKER_RUN_OPTIONS <<< "${DOCKER_RUN_OPTIONS:-}"
"${DOCKER_RUN_OPTIONS[@]}" \
--init \
--sig-proxy=true \
--cap-add=SYS_ADMIN \
${DOCKER_SOCKET_MOUNT:--v /var/run/docker.sock:/var/run/docker.sock} \
-e DOCKER_HOST=${DOCKER_SOCKET_HOST:-unix:///var/run/docker.sock} \
$CONTAINER_OPTIONS \
--env-file <(env | grep -v ${ENV_BLOCKLIST}) \
-e IN_BUILD_CONTAINER=1 \
@ -59,6 +57,5 @@ read -ra DOCKER_RUN_OPTIONS <<< "${DOCKER_RUN_OPTIONS:-}"
--mount "type=volume,source=gocache,destination=/gocache" \
--mount "type=volume,source=cache,destination=/home/.cache" \
--mount "type=volume,source=crates,destination=/home/.cargo/registry" \
--mount "type=volume,source=git-crates,destination=/home/.cargo/git" \
${CONDITIONAL_HOST_MOUNTS} \
-w "${MOUNT_DEST}" "${IMG}" "$@"

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
# shellcheck disable=SC2034
# WARNING: DO NOT EDIT, THIS FILE IS PROBABLY A COPY
@ -75,7 +75,7 @@ fi
TOOLS_REGISTRY_PROVIDER=${TOOLS_REGISTRY_PROVIDER:-gcr.io}
PROJECT_ID=${PROJECT_ID:-istio-testing}
if [[ "${IMAGE_VERSION:-}" == "" ]]; then
IMAGE_VERSION=master-8e6480403f5cf4c9a4cd9d65174d01850e632e1a
IMAGE_VERSION=release-1.19-013f27a57a64f1d22f60c3dcb9b242cf4814065a
fi
if [[ "${IMAGE_NAME:-}" == "" ]]; then
IMAGE_NAME=build-tools
@ -95,19 +95,6 @@ IMG="${IMG:-${TOOLS_REGISTRY_PROVIDER}/${PROJECT_ID}/${IMAGE_NAME}:${IMAGE_VERSI
CONTAINER_CLI="${CONTAINER_CLI:-docker}"
# Try to use the latest cached image we have. Use at your own risk, may have incompatibly-old versions
if [[ "${LATEST_CACHED_IMAGE:-}" != "" ]]; then
prefix="$(<<<"$IMAGE_VERSION" cut -d- -f1)"
query="${TOOLS_REGISTRY_PROVIDER}/${PROJECT_ID}/${IMAGE_NAME}:${prefix}-*"
latest="$("${CONTAINER_CLI}" images --filter=reference="${query}" --format "{{.CreatedAt|json}}~{{.Repository}}:{{.Tag}}~{{.CreatedSince}}" | sort -n -r | head -n1)"
IMG="$(<<<"$latest" cut -d~ -f2)"
if [[ "${IMG}" == "" ]]; then
echo "Attempted to use LATEST_CACHED_IMAGE, but found no images matching ${query}" >&2
exit 1
fi
echo "Using cached image $IMG, created $(<<<"$latest" cut -d~ -f3)" >&2
fi
ENV_BLOCKLIST="${ENV_BLOCKLIST:-^_\|^PATH=\|^GOPATH=\|^GOROOT=\|^SHELL=\|^EDITOR=\|^TMUX=\|^USER=\|^HOME=\|^PWD=\|^TERM=\|^RUBY_\|^GEM_\|^rvm_\|^SSH=\|^TMPDIR=\|^CC=\|^CXX=\|^MAKEFILE_LIST=}"
# Remove functions from the list of exported variables, they mess up with the `env` command.
@ -143,17 +130,12 @@ fi
# echo ${CONDITIONAL_HOST_MOUNTS}
# This function checks if the file exists. If it does, it creates a randomly named host location
# for the file, adds it to the host KUBECONFIG, and creates a mount for it. Note that we use a copy
# of the original file, so that the container can write to it.
# for the file, adds it to the host KUBECONFIG, and creates a mount for it.
add_KUBECONFIG_if_exists () {
if [[ -f "$1" ]]; then
local local_config
local_config="$(mktemp)"
cp "${1}" "${local_config}"
kubeconfig_random="$(od -vAn -N4 -tx /dev/random | tr -d '[:space:]' | cut -c1-8)"
container_kubeconfig+="/config/${kubeconfig_random}:"
CONDITIONAL_HOST_MOUNTS+="--mount type=bind,source=${local_config},destination=/config/${kubeconfig_random} "
CONDITIONAL_HOST_MOUNTS+="--mount type=bind,source=${1},destination=/config/${kubeconfig_random},readonly "
fi
}

View File

@ -8,11 +8,8 @@
# leave room for compiler/linker.
# The number 3G is chosen heuristically to both support large VM and small VM with RBE.
# Startup options cannot be selected via config.
# TODO: Adding just to test android
startup --host_jvm_args=-Xmx3g
common --noenable_bzlmod
fetch --color=yes
run --color=yes
@ -22,37 +19,27 @@ build --workspace_status_command="bash bazel/get_workspace_status"
build --incompatible_strict_action_env
build --java_runtime_version=remotejdk_11
build --tool_java_runtime_version=remotejdk_11
build --java_language_version=11
build --tool_java_language_version=11
build --platform_mappings=bazel/platform_mappings
# silence absl logspam.
build --copt=-DABSL_MIN_LOG_LEVEL=4
# Global C++ standard and common warning suppressions
build --cxxopt=-std=c++20 --host_cxxopt=-std=c++20
build --copt=-Wno-deprecated-declarations
build --define envoy_mobile_listener=enabled
build --experimental_repository_downloader_retries=2
build --enable_platform_specific_config
build --incompatible_merge_fixed_and_default_shell_env
# A workaround for slow ICU download.
build --http_timeout_scaling=6.0
# Pass CC, CXX and LLVM_CONFIG variables from the environment.
# We assume they have stable values, so this won't cause action cache misses.
# Pass PATH, CC, CXX and LLVM_CONFIG variables from the environment.
build --action_env=CC --host_action_env=CC
build --action_env=CXX --host_action_env=CXX
build --action_env=LLVM_CONFIG --host_action_env=LLVM_CONFIG
# Do not pass through PATH however.
# It tends to have machine-specific values, such as dynamically created temp folders.
# This would make it impossible to share remote action cache hits among machines.
# build --action_env=PATH --host_action_env=PATH
# To make our own CI green, we do need that flag on Windows though.
build:windows --action_env=PATH --host_action_env=PATH
build --action_env=PATH --host_action_env=PATH
# Allow stamped caches to bust when local filesystem changes.
# Requires setting `BAZEL_VOLATILE_DIRTY` in the env.
build --action_env=BAZEL_VOLATILE_DIRTY --host_action_env=BAZEL_VOLATILE_DIRTY
# Prevent stamped caches from busting (eg in PRs)
# Requires setting `BAZEL_FAKE_SCM_REVISION` in the env.
build --action_env=BAZEL_FAKE_SCM_REVISION --host_action_env=BAZEL_FAKE_SCM_REVISION
build --enable_platform_specific_config
build --test_summary=terse
build:docs-ci --action_env=DOCS_RST_CHECK=1 --host_action_env=DOCS_RST_CHECK=1
@ -62,21 +49,20 @@ build --incompatible_config_setting_private_default_visibility
build --incompatible_enforce_config_setting_visibility
test --test_verbose_timeout_warnings
test --experimental_ui_max_stdouterr_bytes=11712829 #default 1048576
# Allow tags to influence execution requirements
common --experimental_allow_tags_propagation
build:linux --copt=-fdebug-types-section
# Enable position independent code (this is the default on macOS and Windows)
# (Workaround for https://github.com/bazelbuild/rules_foreign_cc/issues/421)
build:linux --copt=-fPIC
build:linux --cxxopt=-fsized-deallocation --host_cxxopt=-fsized-deallocation
build:linux --copt=-Wno-deprecated-declarations
build:linux --cxxopt=-std=c++17 --host_cxxopt=-std=c++17
build:linux --conlyopt=-fexceptions
build:linux --fission=dbg,opt
build:linux --features=per_object_debug_info
build:linux --action_env=BAZEL_LINKLIBS=-l%:libstdc++.a
build:linux --action_env=BAZEL_LINKOPTS=-lm:-fuse-ld=gold
build:linux --action_env=BAZEL_LINKOPTS=-lm
# We already have absl in the build, define absl=1 to tell googletest to use absl for backtrace.
build --define absl=1
@ -88,89 +74,50 @@ build --@com_googlesource_googleurl//build_config:system_icu=0
build:sanitizer --define tcmalloc=disabled
build:sanitizer --linkopt -ldl
# Common flags for Clang (shared between all clang variants)
build:clang-common --action_env=BAZEL_COMPILER=clang
build:clang-common --linkopt=-fuse-ld=lld
build:clang-common --action_env=CC=clang --host_action_env=CC=clang
build:clang-common --action_env=CXX=clang++ --host_action_env=CXX=clang++
build:clang-common --incompatible_enable_cc_toolchain_resolution=false
# Clang with libc++ (default)
build:clang --config=clang-common
build:clang --config=libc++
build:arm64-clang --config=clang
# Common flags for Clang
build:clang --action_env=BAZEL_COMPILER=clang
build:clang --action_env=CC=clang --action_env=CXX=clang++
build:clang --linkopt=-fuse-ld=lld
# Flags for Clang + PCH
build:clang-pch --spawn_strategy=local
build:clang-pch --define=ENVOY_CLANG_PCH=1
# libstdc++ - currently only used for gcc
build:libstdc++ --@envoy//bazel:libc++=false
build:libstdc++ --@envoy//bazel:libstdc++=true
# Use gold linker for gcc compiler.
build:gcc --config=libstdc++
build:gcc --test_env=HEAPCHECK=
build:gcc --action_env=BAZEL_COMPILER=gcc
build:gcc --action_env=CC=gcc --action_env=CXX=g++
# This is to work around a bug in GCC that makes debug-types-section
# option not play well with fission:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110885
build:gcc --copt=-fno-debug-types-section
# These trigger errors in multiple places both in Envoy dependecies
# and in Envoy code itself when using GCC.
# And in all cases the reports appear to be clear false positives.
build:gcc --copt=-Wno-error=restrict
build:gcc --copt=-Wno-error=uninitialized
build:gcc --cxxopt=-Wno-missing-requires
build:gcc --cxxopt=-Wno-dangling-reference
build:gcc --cxxopt=-Wno-nonnull-compare
build:gcc --incompatible_enable_cc_toolchain_resolution=false
build:gcc --linkopt=-fuse-ld=gold --host_linkopt=-fuse-ld=gold
build:gcc --linkopt=-fuse-ld=gold
# Clang-tidy
# TODO(phlax): enable this, its throwing some errors as well as finding more issues
# build:clang-tidy --@envoy_toolshed//format/clang_tidy:executable=@envoy//tools/clang-tidy
build:clang-tidy --@envoy_toolshed//format/clang_tidy:config=//:clang_tidy_config
build:clang-tidy --aspects @envoy_toolshed//format/clang_tidy:clang_tidy.bzl%clang_tidy_aspect
build:clang-tidy --output_groups=report
build:clang-tidy --build_tag_filters=-notidy
# Basic ASAN/UBSAN that works for gcc or llvm
build:asan-common --config=sanitizer
# Basic ASAN/UBSAN that works for gcc
build:asan --action_env=ENVOY_ASAN=1
build:asan --config=sanitizer
# ASAN install its signal handler, disable ours so the stacktrace will be printed by ASAN
build:asan-common --define signal_trace=disabled
build:asan-common --define ENVOY_CONFIG_ASAN=1
build:asan-common --build_tag_filters=-no_san
build:asan-common --test_tag_filters=-no_san
build:asan-common --copt -fsanitize=address,undefined
build:asan-common --linkopt -fsanitize=address,undefined
# vptr and function sanitizer are enabled in asan if it is set up via bazel/setup_clang.sh.
build:asan-common --copt -fno-sanitize=vptr,function
build:asan-common --linkopt -fno-sanitize=vptr,function
build:asan-common --copt -DADDRESS_SANITIZER=1
build:asan-common --copt -DUNDEFINED_SANITIZER=1
build:asan-common --copt -D__SANITIZE_ADDRESS__
build:asan-common --test_env=ASAN_OPTIONS=handle_abort=1:allow_addr2line=true:check_initialization_order=true:strict_init_order=true:detect_odr_violation=1
build:asan-common --test_env=UBSAN_OPTIONS=halt_on_error=true:print_stacktrace=1
build:asan-common --test_env=ASAN_SYMBOLIZER_PATH
build:asan --define signal_trace=disabled
build:asan --define ENVOY_CONFIG_ASAN=1
build:asan --build_tag_filters=-no_san
build:asan --test_tag_filters=-no_san
build:asan --copt -fsanitize=address,undefined
build:asan --linkopt -fsanitize=address,undefined
# vptr and function sanitizer are enabled in clang-asan if it is set up via bazel/setup_clang.sh.
build:asan --copt -fno-sanitize=vptr,function
build:asan --linkopt -fno-sanitize=vptr,function
build:asan --copt -DADDRESS_SANITIZER=1
build:asan --copt -DUNDEFINED_SANITIZER=1
build:asan --copt -D__SANITIZE_ADDRESS__
build:asan --test_env=ASAN_OPTIONS=handle_abort=1:allow_addr2line=true:check_initialization_order=true:strict_init_order=true:detect_odr_violation=1
build:asan --test_env=UBSAN_OPTIONS=halt_on_error=true:print_stacktrace=1
build:asan --test_env=ASAN_SYMBOLIZER_PATH
# ASAN needs -O1 to get reasonable performance.
build:asan-common --copt -O1
build:asan-common --copt -fno-optimize-sibling-calls
build:asan --copt -O1
build:asan --copt -fno-optimize-sibling-calls
# ASAN config with clang runtime
build:asan --config=asan-common
build:asan --linkopt --rtlib=compiler-rt
build:asan --linkopt --unwindlib=libgcc
build:asan --linkopt=-l:libclang_rt.ubsan_standalone.a
build:asan --linkopt=-l:libclang_rt.ubsan_standalone_cxx.a
build:asan --action_env=ENVOY_UBSAN_VPTR=1
build:asan --copt=-fsanitize=vptr,function
build:asan --linkopt=-fsanitize=vptr,function
build:asan --linkopt='-L/opt/llvm/lib/clang/18/lib/x86_64-unknown-linux-gnu'
# Clang ASAN/UBSAN
build:clang-asan --config=clang
build:clang-asan --config=asan
build:clang-asan --linkopt -fuse-ld=lld
build:clang-asan --linkopt --rtlib=compiler-rt
build:clang-asan --linkopt --unwindlib=libgcc
# macOS
build:macos --cxxopt=-std=c++17 --host_cxxopt=-std=c++17
build:macos --action_env=PATH=/opt/homebrew/bin:/opt/local/bin:/usr/local/bin:/usr/bin:/bin
build:macos --host_action_env=PATH=/opt/homebrew/bin:/opt/local/bin:/usr/local/bin:/usr/bin:/bin
build:macos --define tcmalloc=disabled
@ -185,47 +132,53 @@ build:macos-asan --copt -DGRPC_BAZEL_BUILD
# Dynamic link cause issues like: `dyld: malformed mach-o: load commands size (59272) > 32768`
build:macos-asan --dynamic_mode=off
# Base TSAN config
build:tsan --action_env=ENVOY_TSAN=1
build:tsan --config=sanitizer
build:tsan --define ENVOY_CONFIG_TSAN=1
build:tsan --copt -fsanitize=thread
build:tsan --linkopt -fsanitize=thread
build:tsan --copt -DTHREAD_SANITIZER=1
build:tsan --build_tag_filters=-no_san,-no_tsan
build:tsan --test_tag_filters=-no_san,-no_tsan
# Clang TSAN
build:clang-tsan --action_env=ENVOY_TSAN=1
build:clang-tsan --config=sanitizer
build:clang-tsan --define ENVOY_CONFIG_TSAN=1
build:clang-tsan --copt -fsanitize=thread
build:clang-tsan --linkopt -fsanitize=thread
build:clang-tsan --linkopt -fuse-ld=lld
build:clang-tsan --copt -DTHREAD_SANITIZER=1
build:clang-tsan --build_tag_filters=-no_san,-no_tsan
build:clang-tsan --test_tag_filters=-no_san,-no_tsan
# Needed due to https://github.com/libevent/libevent/issues/777
build:tsan --copt -DEVENT__DISABLE_DEBUG_MODE
build:clang-tsan --copt -DEVENT__DISABLE_DEBUG_MODE
# https://github.com/abseil/abseil-cpp/issues/760
# https://github.com/google/sanitizers/issues/953
build:tsan --test_env="TSAN_OPTIONS=report_atomic_races=0"
build:tsan --test_timeout=120,600,1500,4800
build:clang-tsan --test_env="TSAN_OPTIONS=report_atomic_races=0"
build:clang-tsan --test_timeout=120,600,1500,4800
# Base MSAN config
build:msan --action_env=ENVOY_MSAN=1
build:msan --config=sanitizer
build:msan --build_tag_filters=-no_san
build:msan --test_tag_filters=-no_san
build:msan --define ENVOY_CONFIG_MSAN=1
build:msan --copt -fsanitize=memory
build:msan --linkopt -fsanitize=memory
build:msan --copt -fsanitize-memory-track-origins=2
build:msan --copt -DMEMORY_SANITIZER=1
build:msan --test_env=MSAN_SYMBOLIZER_PATH
# Clang MSAN - this is the base config for remote-msan and docker-msan. To run this config without
# our build image, follow https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo
# with libc++ instruction and provide corresponding `--copt` and `--linkopt` as well.
build:clang-msan --action_env=ENVOY_MSAN=1
build:clang-msan --config=sanitizer
build:clang-msan --build_tag_filters=-no_san
build:clang-msan --test_tag_filters=-no_san
build:clang-msan --define ENVOY_CONFIG_MSAN=1
build:clang-msan --copt -fsanitize=memory
build:clang-msan --linkopt -fsanitize=memory
build:clang-msan --linkopt -fuse-ld=lld
build:clang-msan --copt -fsanitize-memory-track-origins=2
build:clang-msan --copt -DMEMORY_SANITIZER=1
build:clang-msan --test_env=MSAN_SYMBOLIZER_PATH
# MSAN needs -O1 to get reasonable performance.
build:msan --copt -O1
build:msan --copt -fno-optimize-sibling-calls
build:clang-msan --copt -O1
build:clang-msan --copt -fno-optimize-sibling-calls
# Clang with libc++
build:libc++ --config=clang
build:libc++ --action_env=CXXFLAGS=-stdlib=libc++
build:libc++ --action_env=LDFLAGS=-stdlib=libc++
build:libc++ --action_env=BAZEL_CXXOPTS=-stdlib=libc++
build:libc++ --action_env=BAZEL_LINKLIBS=-l%:libc++.a:-l%:libc++abi.a
build:libc++ --action_env=BAZEL_LINKOPTS=-lm:-pthread
build:libc++ --define force_libcpp=enabled
build:libc++ --@envoy//bazel:libc++=true
build:libc++20 --config=libc++
# gRPC has a lot of deprecated-enum-enum-conversion warning. Remove once it is addressed
build:libc++20 --cxxopt=-std=c++20 --copt=-Wno-error=deprecated-enum-enum-conversion
# Optimize build for binary size reduction.
build:sizeopt -c opt --copt -Os
@ -242,6 +195,7 @@ build:coverage --action_env=GCOV=llvm-profdata
build:coverage --copt=-DNDEBUG
# 1.5x original timeout + 300s for trace merger in all categories
build:coverage --test_timeout=390,750,1500,5700
build:coverage --define=dynamic_link_tests=true
build:coverage --define=ENVOY_CONFIG_COVERAGE=1
build:coverage --cxxopt="-DENVOY_CONFIG_COVERAGE=1"
build:coverage --test_env=HEAPCHECK=
@ -260,9 +214,6 @@ build:coverage --define=no_debug_info=1
# `--no-relax` is required for coverage to not err with `relocation R_X86_64_REX_GOTPCRELX`
build:coverage --linkopt=-Wl,-s,--no-relax
build:coverage --test_env=ENVOY_IP_TEST_VERSIONS=v4only
build:coverage --define=dynamic_link_tests=false
# Use custom report generator that also generates HTML
build:coverage --coverage_report_generator=@envoy//tools/coverage:report_generator
build:test-coverage --test_arg="-l trace"
build:test-coverage --test_arg="--log-path /dev/null"
@ -270,45 +221,62 @@ build:test-coverage --test_tag_filters=-nocoverage,-fuzz_target
build:fuzz-coverage --config=plain-fuzzer
build:fuzz-coverage --run_under=@envoy//bazel/coverage:fuzz_coverage_wrapper.sh
build:fuzz-coverage --test_tag_filters=-nocoverage
# Existing fuzz tests don't need a full WASM runtime and in generally we don't really want to
# fuzz dependencies anyways. On the other hand, disabling WASM reduces the build time and
# resources required to build and run the tests.
build:fuzz-coverage --define=wasm=disabled
build:fuzz-coverage --config=fuzz-coverage-config
build:fuzz-coverage-config --//tools/coverage:config=//test:fuzz_coverage_config
build:cache-local --remote_cache=grpc://localhost:9092
# Remote execution: https://docs.bazel.build/versions/master/remote-execution.html
build:rbe-toolchain --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
build:rbe-toolchain --incompatible_enable_cc_toolchain_resolution=false
build:rbe-toolchain-clang --config=rbe-toolchain
build:rbe-toolchain-clang --config=clang
build:rbe-toolchain-clang --platforms=@envoy//bazel/rbe/toolchains:rbe_linux_clang_platform
build:rbe-toolchain-clang --host_platform=@envoy//bazel/rbe/toolchains:rbe_linux_clang_platform
build:rbe-toolchain-clang --crosstool_top=@envoy//bazel/rbe/toolchains/configs/linux/clang/cc:toolchain
build:rbe-toolchain-clang --extra_toolchains=@envoy//bazel/rbe/toolchains/configs/linux/clang/config:cc-toolchain
build:rbe-toolchain-clang --action_env=CC=clang --action_env=CXX=clang++
build:rbe-toolchain-clang --platforms=@envoy_build_tools//toolchains:rbe_linux_clang_platform
build:rbe-toolchain-clang --host_platform=@envoy_build_tools//toolchains:rbe_linux_clang_platform
build:rbe-toolchain-clang --crosstool_top=@envoy_build_tools//toolchains/configs/linux/clang/cc:toolchain
build:rbe-toolchain-clang --extra_toolchains=@envoy_build_tools//toolchains/configs/linux/clang/config:cc-toolchain
build:rbe-toolchain-clang --action_env=CC=clang --action_env=CXX=clang++ --action_env=PATH=/usr/sbin:/usr/bin:/sbin:/bin:/opt/llvm/bin
build:rbe-toolchain-clang-libc++ --config=rbe-toolchain
build:rbe-toolchain-clang-libc++ --platforms=@envoy_build_tools//toolchains:rbe_linux_clang_libcxx_platform
build:rbe-toolchain-clang-libc++ --host_platform=@envoy_build_tools//toolchains:rbe_linux_clang_libcxx_platform
build:rbe-toolchain-clang-libc++ --crosstool_top=@envoy_build_tools//toolchains/configs/linux/clang_libcxx/cc:toolchain
build:rbe-toolchain-clang-libc++ --extra_toolchains=@envoy_build_tools//toolchains/configs/linux/clang_libcxx/config:cc-toolchain
build:rbe-toolchain-clang-libc++ --action_env=CC=clang --action_env=CXX=clang++ --action_env=PATH=/usr/sbin:/usr/bin:/sbin:/bin:/opt/llvm/bin
build:rbe-toolchain-clang-libc++ --action_env=CXXFLAGS=-stdlib=libc++
build:rbe-toolchain-clang-libc++ --action_env=LDFLAGS=-stdlib=libc++
build:rbe-toolchain-clang-libc++ --define force_libcpp=enabled
build:rbe-toolchain-arm64-clang --config=rbe-toolchain
build:rbe-toolchain-arm64-clang --config=clang
build:rbe-toolchain-arm64-clang --platforms=@envoy//bazel/rbe/toolchains:rbe_linux_arm64_clang_platform
build:rbe-toolchain-arm64-clang --host_platform=@envoy//bazel/rbe/toolchains:rbe_linux_arm64_clang_platform
build:rbe-toolchain-arm64-clang --crosstool_top=@envoy//bazel/rbe/toolchains/configs/linux/clang/cc:toolchain
build:rbe-toolchain-arm64-clang --extra_toolchains=@envoy//bazel/rbe/toolchains/configs/linux/clang/config:cc-toolchain-arm64
build:rbe-toolchain-arm64-clang --action_env=CC=clang --action_env=CXX=clang++
# Do not inherit from "clang-asan" to avoid picking up flags from local clang.bazelrc.
build:rbe-toolchain-asan --config=asan
build:rbe-toolchain-asan --linkopt -fuse-ld=lld
build:rbe-toolchain-asan --action_env=ENVOY_UBSAN_VPTR=1
build:rbe-toolchain-asan --copt=-fsanitize=vptr,function
build:rbe-toolchain-asan --linkopt=-fsanitize=vptr,function
build:rbe-toolchain-asan --linkopt='-L/opt/llvm/lib/clang/14.0.0/lib/x86_64-unknown-linux-gnu'
build:rbe-toolchain-asan --linkopt=-l:libclang_rt.ubsan_standalone.a
build:rbe-toolchain-asan --linkopt=-l:libclang_rt.ubsan_standalone_cxx.a
build:rbe-toolchain-msan --linkopt=-L/opt/libcxx_msan/lib
build:rbe-toolchain-msan --linkopt=-Wl,-rpath,/opt/libcxx_msan/lib
build:rbe-toolchain-msan --config=clang-msan
# Sanitizer configs - CI uses the *-common configs directly
# Note: clang config comes from rbe-toolchain-clang to avoid duplication
build:rbe-toolchain-tsan --linkopt=-L/opt/libcxx_tsan/lib
build:rbe-toolchain-tsan --linkopt=-Wl,-rpath,/opt/libcxx_tsan/lib
build:rbe-toolchain-tsan --config=clang-tsan
build:rbe-toolchain-gcc --config=rbe-toolchain
build:rbe-toolchain-gcc --platforms=@envoy//bazel/rbe/toolchains:rbe_linux_gcc_platform
build:rbe-toolchain-gcc --host_platform=@envoy//bazel/rbe/toolchains:rbe_linux_gcc_platform
build:rbe-toolchain-gcc --crosstool_top=@envoy//bazel/rbe/toolchains/configs/linux/gcc/cc:toolchain
build:rbe-toolchain-gcc --extra_toolchains=@envoy//bazel/rbe/toolchains/configs/linux/gcc/config:cc-toolchain
build:rbe-toolchain-gcc --platforms=@envoy_build_tools//toolchains:rbe_linux_gcc_platform
build:rbe-toolchain-gcc --host_platform=@envoy_build_tools//toolchains:rbe_linux_gcc_platform
build:rbe-toolchain-gcc --crosstool_top=@envoy_build_tools//toolchains/configs/linux/gcc/cc:toolchain
build:rbe-toolchain-gcc --extra_toolchains=@envoy_build_tools//toolchains/configs/linux/gcc/config:cc-toolchain
build:rbe-toolchain-msvc-cl --host_platform=@envoy_build_tools//toolchains:rbe_windows_msvc_cl_platform
build:rbe-toolchain-msvc-cl --platforms=@envoy_build_tools//toolchains:rbe_windows_msvc_cl_platform
build:rbe-toolchain-msvc-cl --crosstool_top=@envoy_build_tools//toolchains/configs/windows/msvc-cl/cc:toolchain
build:rbe-toolchain-msvc-cl --extra_toolchains=@envoy_build_tools//toolchains/configs/windows/msvc-cl/config:cc-toolchain
build:rbe-toolchain-clang-cl --host_platform=@envoy_build_tools//toolchains:rbe_windows_clang_cl_platform
build:rbe-toolchain-clang-cl --platforms=@envoy_build_tools//toolchains:rbe_windows_clang_cl_platform
build:rbe-toolchain-clang-cl --crosstool_top=@envoy_build_tools//toolchains/configs/windows/clang-cl/cc:toolchain
build:rbe-toolchain-clang-cl --extra_toolchains=@envoy_build_tools//toolchains/configs/windows/clang-cl/config:cc-toolchain
build:remote --spawn_strategy=remote,sandboxed,local
build:remote --strategy=Javac=remote,sandboxed,local
@ -320,7 +288,6 @@ build:remote-windows --spawn_strategy=remote,local
build:remote-windows --strategy=Javac=remote,local
build:remote-windows --strategy=Closure=remote,local
build:remote-windows --strategy=Genrule=remote,local
build:remote-windows --strategy=CppLink=local
build:remote-windows --remote_timeout=7200
build:remote-windows --google_default_credentials=true
build:remote-windows --remote_download_toplevel
@ -328,26 +295,23 @@ build:remote-windows --remote_download_toplevel
build:remote-clang --config=remote
build:remote-clang --config=rbe-toolchain-clang
build:remote-arm64-clang --config=remote
build:remote-arm64-clang --config=rbe-toolchain-arm64-clang
build:remote-clang-libc++ --config=remote
build:remote-clang-libc++ --config=rbe-toolchain-clang-libc++
build:remote-gcc --config=remote
build:remote-gcc --config=gcc
build:remote-gcc --config=rbe-toolchain-gcc
build:remote-asan --config=remote
build:remote-asan --config=rbe-toolchain-clang
build:remote-asan --config=asan
build:remote-asan --config=rbe-toolchain-clang-libc++
build:remote-asan --config=rbe-toolchain-asan
build:remote-msan --config=remote
build:remote-msan --config=rbe-toolchain-clang
build:remote-msan --config=msan
build:remote-msan --config=rbe-toolchain-clang-libc++
build:remote-msan --config=rbe-toolchain-msan
build:remote-tsan --config=remote
build:remote-tsan --config=rbe-toolchain-clang
build:remote-tsan --config=tsan
build:remote-tsan --config=rbe-toolchain-clang-libc++
build:remote-tsan --config=rbe-toolchain-tsan
build:remote-msvc-cl --config=remote-windows
build:remote-msvc-cl --config=msvc-cl
@ -371,15 +335,14 @@ build:compile-time-options --define=deprecated_features=disabled
build:compile-time-options --define=tcmalloc=gperftools
build:compile-time-options --define=zlib=ng
build:compile-time-options --define=uhv=enabled
# gRPC has a lot of deprecated-enum-enum-conversion warnings with C++20
build:compile-time-options --copt=-Wno-error=deprecated-enum-enum-conversion
build:compile-time-options --config=libc++20
build:compile-time-options --test_env=ENVOY_HAS_EXTRA_EXTENSIONS=true
build:compile-time-options --@envoy//bazel:http3=False
build:compile-time-options --@envoy//source/extensions/filters/http/kill_request:enabled
# Docker sandbox
# NOTE: Update this from https://github.com/envoyproxy/envoy-build-tools/blob/main/toolchains/rbe_toolchains_config.bzl#L8
build:docker-sandbox --experimental_docker_image=envoyproxy/envoy-build-ubuntu:f4a881a1205e8e6db1a57162faf3df7aed88eae8@sha256:b10346fe2eee41733dbab0e02322c47a538bf3938d093a5daebad9699860b814
build:docker-sandbox --experimental_docker_image=envoyproxy/envoy-build-ubuntu:fdd65c6270a8507a18d5acd6cf19a18cb695e4fa@sha256:3c8a3ce6f90dcfb5d09dc8f79bb01404d3526d420061f9a176e0a8e91e1e573e
build:docker-sandbox --spawn_strategy=docker
build:docker-sandbox --strategy=Javac=docker
build:docker-sandbox --strategy=Closure=docker
@ -391,37 +354,39 @@ build:docker-sandbox --experimental_enable_docker_sandbox
build:docker-clang --config=docker-sandbox
build:docker-clang --config=rbe-toolchain-clang
build:docker-clang-libc++ --config=docker-sandbox
build:docker-clang-libc++ --config=rbe-toolchain-clang-libc++
build:docker-gcc --config=docker-sandbox
build:docker-gcc --config=gcc
build:docker-gcc --config=rbe-toolchain-gcc
build:docker-asan --config=docker-sandbox
build:docker-asan --config=rbe-toolchain-clang
build:docker-asan --config=asan
build:docker-asan --config=rbe-toolchain-clang-libc++
build:docker-asan --config=rbe-toolchain-asan
build:docker-msan --config=docker-sandbox
build:docker-msan --config=rbe-toolchain-clang
build:docker-msan --config=msan
build:docker-msan --config=rbe-toolchain-clang-libc++
build:docker-msan --config=rbe-toolchain-msan
build:docker-tsan --config=docker-sandbox
build:docker-tsan --config=rbe-toolchain-clang
build:docker-tsan --config=tsan
build:docker-tsan --config=rbe-toolchain-clang-libc++
build:docker-tsan --config=rbe-toolchain-tsan
# CI configurations
build:remote-ci --config=ci
build:remote-ci --remote_download_minimal
# Note this config is used by mobile CI also.
common:ci --noshow_progress
common:ci --noshow_loading_progress
common:ci --test_output=errors
build:ci --noshow_progress
build:ci --noshow_loading_progress
build:ci --test_output=errors
# Fuzz builds
# Shared fuzzing configuration.
build:fuzzing --define=ENVOY_CONFIG_ASAN=1
build:fuzzing --copt=-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
build:fuzzing --config=libc++
# Fuzzing without ASAN. This is useful for profiling fuzzers without any ASAN artifacts.
build:plain-fuzzer --config=fuzzing
@ -432,16 +397,13 @@ build:plain-fuzzer --define=FUZZING_ENGINE=libfuzzer
build:plain-fuzzer --copt=-fsanitize=fuzzer-no-link
build:plain-fuzzer --linkopt=-fsanitize=fuzzer-no-link
# ASAN fuzzer
build:asan-fuzzer --config=plain-fuzzer
build:asan-fuzzer --config=asan
build:asan-fuzzer --config=clang-asan
build:asan-fuzzer --copt=-fno-omit-frame-pointer
# Remove UBSAN halt_on_error to avoid crashing on protobuf errors.
build:asan-fuzzer --test_env=UBSAN_OPTIONS=print_stacktrace=1
build:asan-fuzzer --linkopt=-lc++
build:oss-fuzz --config=fuzzing
build:oss-fuzz --config=libc++
build:oss-fuzz --define=FUZZING_ENGINE=oss-fuzz
build:oss-fuzz --@rules_fuzzing//fuzzing:cc_engine_instrumentation=oss-fuzz
build:oss-fuzz --@rules_fuzzing//fuzzing:cc_engine_sanitizer=none
@ -466,7 +428,7 @@ build:windows --define hot_restart=disabled
build:windows --define tcmalloc=disabled
build:windows --define wasm=disabled
build:windows --define manual_stamp=manual_stamp
build:windows --cxxopt="/std:c++20"
build:windows --cxxopt="/std:c++17"
build:windows --output_groups=+pdb_file
# TODO(wrowe,sunjayBhatia): Resolve bugs upstream in curl and rules_foreign_cc
@ -509,16 +471,14 @@ build:windows --features=static_link_msvcrt
build:windows --dynamic_mode=off
# RBE (Google)
build:cache-google --google_default_credentials=true
build:cache-google --remote_cache=grpcs://remotebuildexecution.googleapis.com
build:cache-google --remote_instance_name=projects/envoy-ci/instances/default_instance
build:cache-google --remote_timeout=7200
build:rbe-google --google_default_credentials=true
build:rbe-google --remote_cache=grpcs://remotebuildexecution.googleapis.com
build:rbe-google --remote_executor=grpcs://remotebuildexecution.googleapis.com
build:rbe-google --config=cache-google
build:rbe-google --remote_timeout=7200
build:rbe-google --remote_instance_name=projects/envoy-ci/instances/default_instance
build:rbe-google-bes --bes_backend=grpcs://buildeventservice.googleapis.com
build:rbe-google-bes --bes_results_url=https://source.cloud.google.com/results/invocations/
build:rbe-google-bes --bes_upload_mode=fully_async
# RBE (Engflow mobile)
build:rbe-engflow --google_default_credentials=false
@ -527,41 +487,24 @@ build:rbe-engflow --remote_executor=grpcs://envoy.cluster.engflow.com
build:rbe-engflow --bes_backend=grpcs://envoy.cluster.engflow.com/
build:rbe-engflow --bes_results_url=https://envoy.cluster.engflow.com/invocation/
build:rbe-engflow --credential_helper=*.engflow.com=%workspace%/bazel/engflow-bazel-credential-helper.sh
build:rbe-engflow --grpc_keepalive_time=60s
build:rbe-engflow --grpc_keepalive_timeout=30s
build:rbe-engflow --grpc_keepalive_time=30s
build:rbe-engflow --remote_timeout=3600s
build:rbe-engflow --bes_timeout=3600s
build:rbe-engflow --bes_upload_mode=fully_async
build:rbe-engflow --nolegacy_important_outputs
# RBE (Engflow Envoy)
common:common-envoy-engflow --google_default_credentials=false
common:common-envoy-engflow --credential_helper=*.engflow.com=%workspace%/bazel/engflow-bazel-credential-helper.sh
common:common-envoy-engflow --grpc_keepalive_time=60s
common:common-envoy-engflow --grpc_keepalive_timeout=30s
common:cache-envoy-engflow --remote_cache=grpcs://mordenite.cluster.engflow.com
common:cache-envoy-engflow --remote_timeout=3600s
# common:cache-envoy-engflow --remote_instance_name=llvm-18
common:bes-envoy-engflow --bes_backend=grpcs://mordenite.cluster.engflow.com/
common:bes-envoy-engflow --bes_results_url=https://mordenite.cluster.engflow.com/invocation/
common:bes-envoy-engflow --bes_timeout=3600s
common:bes-envoy-engflow --bes_upload_mode=fully_async
common:bes-envoy-engflow --nolegacy_important_outputs
common:rbe-envoy-engflow --remote_executor=grpcs://mordenite.cluster.engflow.com
common:rbe-envoy-engflow --remote_default_exec_properties=container-image=docker://gcr.io/envoy-ci/envoy-build@sha256:95d7afdea0f0f8881e88fa5e581db4f50907d0745ac8d90e00357ac1a316abe5
common:rbe-envoy-engflow --jobs=200
common:rbe-envoy-engflow --define=engflow_rbe=true
common:remote-envoy-engflow --config=common-envoy-engflow
common:remote-envoy-engflow --config=cache-envoy-engflow
common:remote-envoy-engflow --config=rbe-envoy-engflow
common:remote-cache-envoy-engflow --config=common-envoy-engflow
common:remote-cache-envoy-engflow --config=cache-envoy-engflow
# Specifies the rustfmt.toml for all rustfmt_test targets.
build --@rules_rust//rust/settings:rustfmt.toml=//:rustfmt.toml
build:cache-envoy-engflow --google_default_credentials=false
build:cache-envoy-engflow --remote_cache=grpcs://morganite.cluster.engflow.com
build:cache-envoy-engflow --remote_timeout=3600s
build:cache-envoy-engflow --credential_helper=*.engflow.com=%workspace%/bazel/engflow-bazel-credential-helper.sh
build:cache-envoy-engflow --grpc_keepalive_time=30s
build:bes-envoy-engflow --bes_backend=grpcs://morganite.cluster.engflow.com/
build:bes-envoy-engflow --bes_results_url=https://morganite.cluster.engflow.com/invocation/
build:bes-envoy-engflow --bes_timeout=3600s
build:bes-envoy-engflow --bes_upload_mode=fully_async
build:rbe-envoy-engflow --config=cache-envoy-engflow
build:rbe-envoy-engflow --config=bes-envoy-engflow
build:rbe-envoy-engflow --remote_executor=grpcs://morganite.cluster.engflow.com
build:rbe-envoy-engflow --remote_default_exec_properties=container-image=docker://docker.io/envoyproxy/envoy-build-ubuntu:fdd65c6270a8507a18d5acd6cf19a18cb695e4fa@sha256:3c8a3ce6f90dcfb5d09dc8f79bb01404d3526d420061f9a176e0a8e91e1e573e
#############################################################################
# debug: Various Bazel debugging flags
@ -585,7 +528,6 @@ common:debug --config=debug-sandbox
common:debug --config=debug-coverage
common:debug --config=debug-tests
try-import %workspace%/repo.bazelrc
try-import %workspace%/clang.bazelrc
try-import %workspace%/user.bazelrc
try-import %workspace%/local_tsan.bazelrc

58
extensions/BUILD Normal file
View File

@ -0,0 +1,58 @@
# 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/wasm:wasm.bzl",
"envoy_wasm_cc_binary",
)
load(
"@envoy//bazel:envoy_build_system.bzl",
"envoy_package",
)
envoy_package()
WASM_LINKOPTS = [
"-s INITIAL_MEMORY=8MB",
]
envoy_wasm_cc_binary(
name = "metadata_exchange.wasm",
srcs = [
"//extensions/common:context.cc",
"//extensions/common:context.h",
"//extensions/common:proto_util.cc",
"//extensions/common:proto_util.h",
"//extensions/common:util.cc",
"//extensions/common:util.h",
"//extensions/common/wasm:base64.h",
"//extensions/metadata_exchange:plugin.cc",
"//extensions/metadata_exchange:plugin.h",
],
copts = ["-UNULL_PLUGIN"],
linkopts = WASM_LINKOPTS,
tags = ["manual"],
deps = [
"//extensions/common:node_info_fb_cc",
"//extensions/common/wasm:json_util",
"//extensions/metadata_exchange:declare_property_proto_cc",
"//external:abseil_strings",
"//external:abseil_time",
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics_full",
"@proxy_wasm_cpp_sdk//contrib:contrib_lib",
],
)

View File

@ -0,0 +1,29 @@
licenses(["notice"]) # Apache 2
load(
"@envoy//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_package",
)
envoy_package()
envoy_cc_library(
name = "access_log_policy_lib",
srcs = [
"config.cc",
"plugin.cc",
],
hdrs = [
"plugin.h",
],
repository = "@envoy",
visibility = ["//visibility:public"],
deps = [
"//extensions/access_log_policy/config/v1alpha1:access_log_policy_config_cc_proto",
"//extensions/common:context",
"//extensions/common:istio_dimensions",
"@envoy//source/common/common:base64_lib",
"@proxy_wasm_cpp_host//:null_lib",
],
)

View File

@ -0,0 +1,33 @@
/* 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 "extensions/access_log_policy/plugin.h"
#include "source/common/common/base64.h"
namespace proxy_wasm {
namespace null_plugin {
namespace AccessLogPolicy {
namespace Plugin {
NullPluginRegistry* context_registry_{};
} // namespace Plugin
// Registration glue
RegisterNullVmPluginFactory register_access_log_policy_filter("envoy.wasm.access_log_policy", []() {
return std::make_unique<NullPlugin>(Plugin::context_registry_);
});
} // namespace AccessLogPolicy
} // namespace null_plugin
} // namespace proxy_wasm

View File

@ -0,0 +1,2 @@
access_log_policy_config.pb.h
access_log_policy_config.pb.cc

View File

@ -0,0 +1,32 @@
# 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.
#
################################################################################
#
cc_proto_library(
name = "access_log_policy_config_cc_proto",
visibility = [
"//extensions/access_log_policy:__pkg__",
],
deps = ["access_log_policy_config_proto"],
)
proto_library(
name = "access_log_policy_config_proto",
srcs = ["access_log_policy_config.proto"],
deps = [
"@com_google_protobuf//:duration_proto",
],
)

View File

@ -0,0 +1,62 @@
---
title: AccessLogPolicy Config
description: Configuration for AccessLogPolicy Filter.
location: https://istio.io/docs/reference/config/proxy_extensions/accesslogpolicy.html
layout: protoc-gen-docs
generator: protoc-gen-docs
weight: 20
number_of_entries: 1
---
<p>Accesslog Policy plugin is a stateful http log sampler.
It decides whether a request is logged based on the following rules.</p>
<ol>
<li>All requests resulting in errors are logged.</li>
<li>First successful request within log_window_duration from a specific
source ip (source principal) is logged.
The plugin records its decision in the istio.access_log_policy attribute with
a value of &ldquo;no&rdquo;. A downstream plugin may honor the the attribute. For
example, Stackdriver plugin will not produce an access log entry if this
attribute is set.</li>
</ol>
<h2 id="AccessLogPolicyConfig">AccessLogPolicyConfig</h2>
<section>
<p>Top level Config for Access Log Policy Config Filter.</p>
<table class="message-fields">
<thead>
<tr>
<th>Field</th>
<th>Type</th>
<th>Description</th>
<th>Required</th>
</tr>
</thead>
<tbody>
<tr id="AccessLogPolicyConfig-log_window_duration">
<td><code>log_window_duration</code></td>
<td><code><a href="https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#duration">Duration</a></code></td>
<td>
<p>Optional. Allows specifying logging window for successful requests.
The default duration is <code>12h</code>.</p>
</td>
<td>
No
</td>
</tr>
<tr id="AccessLogPolicyConfig-max_client_cache_size">
<td><code>max_client_cache_size</code></td>
<td><code>int32</code></td>
<td>
<p>Optional. Allows specifying max client cache size.
The default is 500 entries.</p>
</td>
<td>
No
</td>
</tr>
</tbody>
</table>
</section>

View File

@ -0,0 +1,49 @@
/* 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";
// clang-format off
// $title: AccessLogPolicy Config
// $description: Configuration for AccessLogPolicy Filter.
// $location: https://istio.io/docs/reference/config/proxy_extensions/accesslogpolicy.html
// $weight: 20
// clang-format on
// Accesslog Policy plugin is a stateful http log sampler.
// It decides whether a request is logged based on the following rules.
// 1. All requests resulting in errors are logged.
// 2. First successful request within log_window_duration from a specific
// source ip (source principal) is logged.
// The plugin records its decision in the istio.access_log_policy attribute with
// a value of "no". A downstream plugin may honor the the attribute. For
// example, Stackdriver plugin will not produce an access log entry if this
// attribute is set.
package accesslogpolicy.config.v1alpha1;
import "google/protobuf/duration.proto";
// Top level Config for Access Log Policy Config Filter.
message AccessLogPolicyConfig {
// next id: 3
// Optional. Allows specifying logging window for successful requests.
// The default duration is `12h`.
google.protobuf.Duration log_window_duration = 1;
// Optional. Allows specifying max client cache size.
// The default is 500 entries.
int32 max_client_cache_size = 2;
}

View File

@ -0,0 +1,174 @@
/* 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 "extensions/access_log_policy/plugin.h"
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "extensions/common/istio_dimensions.h"
#include "google/protobuf/util/json_util.h"
#include "google/protobuf/util/time_util.h"
#ifndef NULL_PLUGIN
#include "base64.h"
#else
#include "source/common/common/base64.h"
namespace proxy_wasm {
namespace null_plugin {
namespace AccessLogPolicy {
namespace Plugin {
using google::protobuf::util::JsonParseOptions;
using proxy_wasm::WasmHeaderMapType;
PROXY_WASM_NULL_PLUGIN_REGISTRY;
#endif
namespace {
bool setFilterStateValue(bool log) {
auto r = setFilterStateStringValue(::Wasm::Common::kAccessLogPolicyKey, log ? "yes" : "no");
if (r != WasmResult::Ok) {
logWarn(toString(r));
return false;
}
return true;
}
} // namespace
constexpr long long kDefaultLogWindowDurationNanoseconds = 43200000000000; // 12h
constexpr std::string_view kSource = "source";
constexpr std::string_view kAddress = "address";
constexpr std::string_view kConnection = "connection";
constexpr std::string_view kUriSanPeerCertificate = "uri_san_peer_certificate";
constexpr std::string_view kResponse = "response";
constexpr std::string_view kCode = "code";
constexpr std::string_view kGrpcStatus = "grpc_status";
static RegisterContextFactory register_AccessLogPolicy(CONTEXT_FACTORY(PluginContext),
ROOT_FACTORY(PluginRootContext));
bool PluginRootContext::onConfigure(size_t size) {
initialized_ = configure(size);
return true;
}
bool PluginRootContext::configure(size_t configuration_size) {
auto configuration_data =
getBufferBytes(WasmBufferType::PluginConfiguration, 0, configuration_size);
auto configuration = configuration_data->toString();
JsonParseOptions json_options;
json_options.ignore_unknown_fields = true;
const auto status = JsonStringToMessage(configuration, &config_, json_options);
if (!status.ok()) {
logWarn("Cannot parse AccessLog plugin configuration JSON string " + configuration + ", " +
std::string(status.message()));
return false;
}
if (config_.has_log_window_duration()) {
log_time_duration_nanos_ =
::google::protobuf::util::TimeUtil::DurationToNanoseconds(config_.log_window_duration());
} else {
log_time_duration_nanos_ = kDefaultLogWindowDurationNanoseconds;
}
if (config_.max_client_cache_size() > 0) {
max_client_cache_size_ = config_.max_client_cache_size();
}
return true;
}
void PluginRootContext::updateLastLogTimeNanos(const Wasm::Common::IstioDimensions& key,
long long last_log_time_nanos) {
if (int32_t(cache_.size()) > max_client_cache_size_) {
auto it = cache_.begin();
cache_.erase(cache_.begin(), std::next(it, max_client_cache_size_ / 4));
logDebug(absl::StrCat("cleaned cache, new cache_size:", cache_.size()));
}
cache_[key] = last_log_time_nanos;
}
void PluginContext::onLog() {
if (!rootContext()->initialized()) {
return;
}
// Check if request is a failure.
if (isRequestFailed()) {
LOG_TRACE("Setting logging to true as we got error log");
setFilterStateValue(true);
return;
}
// If request is not a failure, check cache to see if it should be logged or
// not, based on last time a successful request was logged for this client ip
// and principal combination.
std::string source_ip = "";
getValue({kSource, kAddress}, &source_ip);
std::string source_principal = "";
getValue({kConnection, kUriSanPeerCertificate}, &source_principal);
istio_dimensions_.set_downstream_ip(source_ip);
istio_dimensions_.set_source_principal(source_principal);
long long last_log_time_nanos = lastLogTimeNanos();
auto cur = static_cast<long long>(getCurrentTimeNanoseconds());
if ((cur - last_log_time_nanos) > logTimeDurationNanos()) {
LOG_TRACE(
absl::StrCat("Setting logging to true as its outside of log windown. SourceIp: ", source_ip,
" SourcePrincipal: ", source_principal, " Window: ", logTimeDurationNanos()));
if (setFilterStateValue(true)) {
updateLastLogTimeNanos(cur);
}
return;
}
setFilterStateValue(false);
}
bool PluginContext::isRequestFailed() {
// Check if HTTP request is a failure.
int64_t http_response_code = 0;
if (getValue({kResponse, kCode}, &http_response_code) && http_response_code >= 400) {
return true;
}
// Check if gRPC request is a failure.
int64_t grpc_response_code = 0;
if (::Wasm::Common::kGrpcContentTypes.count(
getHeaderMapValue(WasmHeaderMapType::RequestHeaders,
::Wasm::Common::kContentTypeHeaderKey)
->toString()) != 0 &&
getValue({kResponse, kGrpcStatus}, &grpc_response_code) && grpc_response_code != 0) {
return true;
}
return false;
}
#ifdef NULL_PLUGIN
} // namespace Plugin
} // namespace AccessLogPolicy
} // namespace null_plugin
} // namespace proxy_wasm
#endif

View File

@ -0,0 +1,106 @@
/* 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 "absl/container/flat_hash_map.h"
#include "extensions/access_log_policy/config/v1alpha1/access_log_policy_config.pb.h"
#include "extensions/common/context.h"
#include "extensions/common/istio_dimensions.h"
#ifndef NULL_PLUGIN
#include <assert.h>
#define ASSERT(_X) assert(_X)
#include "proxy_wasm_intrinsics.h"
static const std::string EMPTY_STRING;
#else
#include "include/proxy-wasm/null_plugin.h"
namespace proxy_wasm {
namespace null_plugin {
namespace AccessLogPolicy {
namespace Plugin {
#endif
const size_t DefaultClientCacheMaxSize = 500;
// PluginRootContext is the root context for all streams processed by the
// thread. It has the same lifetime as the filter instance and acts as target
// for interactions that outlives individual stream, e.g. timer, async calls.
class PluginRootContext : public RootContext {
public:
PluginRootContext(uint32_t id, std::string_view root_id) : RootContext(id, root_id) {}
~PluginRootContext() = default;
bool onConfigure(size_t) override;
bool configure(size_t);
long long lastLogTimeNanos(const Wasm::Common::IstioDimensions& key) {
if (cache_.contains(key)) {
return cache_[key];
}
return 0;
}
void updateLastLogTimeNanos(const Wasm::Common::IstioDimensions& key,
long long last_log_time_nanos);
long long logTimeDurationNanos() { return log_time_duration_nanos_; };
bool initialized() const { return initialized_; };
private:
accesslogpolicy::config::v1alpha1::AccessLogPolicyConfig config_;
// Cache storing last log time by a client.
absl::flat_hash_map<Wasm::Common::IstioDimensions, long long> cache_;
int32_t max_client_cache_size_ = DefaultClientCacheMaxSize;
long long log_time_duration_nanos_;
bool initialized_ = false;
};
// Per-stream context.
class PluginContext : public Context {
public:
explicit PluginContext(uint32_t id, RootContext* root) : Context(id, root) {}
void onLog() override;
private:
inline PluginRootContext* rootContext() {
return dynamic_cast<PluginRootContext*>(this->root());
};
inline long long lastLogTimeNanos() {
return rootContext()->lastLogTimeNanos(istio_dimensions_);
};
inline void updateLastLogTimeNanos(long long last_log_time_nanos) {
rootContext()->updateLastLogTimeNanos(istio_dimensions_, last_log_time_nanos);
};
inline long long logTimeDurationNanos() { return rootContext()->logTimeDurationNanos(); };
bool isRequestFailed();
Wasm::Common::IstioDimensions istio_dimensions_;
};
#ifdef NULL_PLUGIN
} // namespace Plugin
} // namespace AccessLogPolicy
} // namespace null_plugin
} // namespace proxy_wasm
#endif

View File

@ -15,28 +15,185 @@
################################################################################
#
licenses(["notice"])
load(
"@envoy//bazel:envoy_build_system.bzl",
"envoy_cc_benchmark_binary",
"envoy_cc_library",
"envoy_cc_test",
"envoy_package",
)
load(
"@com_github_google_flatbuffers//:build_defs.bzl",
"DEFAULT_FLATC_ARGS",
"flatbuffer_library_public",
)
load(
"@envoy//test/extensions:extensions_build_system.bzl",
"envoy_extension_cc_benchmark_binary",
"envoy_extension_cc_test",
)
package(default_visibility = ["//visibility:public"])
envoy_package()
licenses(["notice"])
envoy_cc_library(
name = "context",
srcs = [
"context.cc",
],
hdrs = [
"context.h",
"istio_dimensions.h",
],
repository = "@envoy",
visibility = ["//visibility:public"],
deps = [
":node_info_fb_cc",
":util",
"@proxy_wasm_cpp_host//:null_lib",
],
)
envoy_cc_library(
name = "proto_util",
srcs = [
"proto_util.cc",
],
hdrs = [
"proto_util.h",
],
repository = "@envoy",
visibility = ["//visibility:public"],
deps = [
":node_info_fb_cc",
":util",
"@com_google_protobuf//:protobuf",
"@proxy_wasm_cpp_host//:null_lib",
],
)
envoy_cc_library(
name = "util",
srcs = [
"util.cc",
],
hdrs = [
"util.h",
],
repository = "@envoy",
visibility = ["//visibility:public"],
deps = [
"@proxy_wasm_cpp_host//:null_lib",
],
)
envoy_cc_library(
name = "istio_dimensions",
hdrs = [
"istio_dimensions.h",
],
repository = "@envoy",
visibility = ["//visibility:public"],
)
envoy_extension_cc_test(
name = "proto_util_test",
size = "small",
srcs = ["proto_util_test.cc"],
extension_names = ["envoy.wasm.runtime.null"],
repository = "@envoy",
deps = [
":node_info_fb_cc",
":proto_util",
],
)
envoy_cc_test(
name = "util_test",
size = "small",
srcs = ["util_test.cc"],
repository = "@envoy",
deps = [
":util",
],
)
envoy_cc_test(
name = "istio_dimensions_test",
size = "small",
srcs = ["istio_dimensions_test.cc"],
external_deps = ["abseil_hash_testing"],
repository = "@envoy",
deps = [
":istio_dimensions",
],
)
envoy_extension_cc_benchmark_binary(
name = "proto_util_speed_test",
srcs = ["proto_util_speed_test.cc"],
extension_names = ["envoy.wasm.runtime.null"],
external_deps = [
"benchmark",
],
repository = "@envoy",
deps = [
":metadata_object_lib",
":node_info_fb_cc",
":proto_util",
"@envoy//source/common/stream_info:filter_state_lib",
"@envoy//source/extensions/filters/common/expr:cel_state_lib",
"@envoy//test/test_common:status_utility_lib",
],
)
flatbuffer_library_public(
name = "node_info_fbs",
srcs = ["node_info.fbs"],
outs = [
"node_info_bfbs_generated.h",
"node_info_generated.h",
],
flatc_args = DEFAULT_FLATC_ARGS + ["--bfbs-gen-embed"],
language_flag = "-c",
)
cc_library(
name = "node_info_fb_cc",
srcs = [":node_info_fbs"],
hdrs = [":node_info_fbs"],
features = ["-parse_headers"],
linkstatic = True,
deps = [
"@com_github_google_flatbuffers//:flatbuffers",
"@com_github_google_flatbuffers//:runtime_cc",
],
)
envoy_cc_library(
name = "json_util",
srcs = [
"//extensions/common/wasm:json_util.cc",
],
hdrs = [
"//extensions/common/wasm:json_util.h",
"//extensions/common/wasm:nlohmann_json_hpp",
],
repository = "@envoy",
visibility = ["//visibility:public"],
)
envoy_cc_library(
name = "metadata_object_lib",
srcs = ["metadata_object.cc"],
hdrs = ["metadata_object.h"],
repository = "@envoy",
visibility = ["//visibility:public"],
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",
":node_info_fb_cc",
"@envoy//envoy/network:filter_interface",
"@envoy//source/exe:envoy_common_lib",
],
)
@ -46,6 +203,6 @@ envoy_cc_test(
repository = "@envoy",
deps = [
":metadata_object_lib",
"@envoy//envoy/registry",
"@envoy//test/mocks/ssl:ssl_mocks",
],
)

View File

@ -0,0 +1,569 @@
/* 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 "extensions/common/context.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "extensions/common/node_info_bfbs_generated.h"
#include "extensions/common/util.h"
#include "flatbuffers/util.h"
// WASM_PROLOG
#ifndef NULL_PLUGIN
#include "proxy_wasm_intrinsics.h"
#else // NULL_PLUGIN
#include "include/proxy-wasm/null_plugin.h"
using proxy_wasm::WasmHeaderMapType;
using proxy_wasm::null_plugin::getHeaderMapValue;
using proxy_wasm::null_plugin::getProperty;
using proxy_wasm::null_plugin::getValue;
#endif // NULL_PLUGIN
// END WASM_PROLOG
namespace Wasm {
namespace Common {
const char kBlackHoleCluster[] = "BlackHoleCluster";
const char kPassThroughCluster[] = "PassthroughCluster";
const char kBlackHoleRouteName[] = "block_all";
const char kPassThroughRouteName[] = "allow_any";
const char kInboundPassthroughClusterIpv4[] = "InboundPassthroughClusterIpv4";
const char kInboundPassthroughClusterIpv6[] = "InboundPassthroughClusterIpv6";
// Well-known name for the grpc_stats filter.
constexpr std::string_view GrpcStatsName = "envoy.filters.http.grpc_stats";
namespace {
// Get destination service host and name based on destination cluster metadata
// and host header.
// * If cluster name is one of passthrough and blackhole clusters, use cluster
// name as destination service name and host header as destination host.
// * Otherwise, try fetching cluster metadata for destination service name and
// host. If cluster metadata is not available, set destination service name
// the same as destination service host.
void populateDestinationService(bool outbound, bool use_host_header, RequestInfo* request_info) {
if (use_host_header) {
request_info->destination_service_host = request_info->url_host;
} else {
request_info->destination_service_host = outbound ? "unknown" : getServiceNameFallback();
}
// override the cluster name if this is being sent to the
// blackhole or passthrough cluster
const std::string& route_name = request_info->route_name;
if (route_name == kBlackHoleRouteName) {
request_info->destination_service_name = kBlackHoleCluster;
return;
} else if (route_name == kPassThroughRouteName) {
request_info->destination_service_name = kPassThroughCluster;
return;
}
const std::string& cluster_name = request_info->upstream_cluster;
if (cluster_name == kBlackHoleCluster || cluster_name == kPassThroughCluster ||
cluster_name == kInboundPassthroughClusterIpv4 ||
cluster_name == kInboundPassthroughClusterIpv6) {
request_info->destination_service_name = cluster_name;
return;
}
// Get destination service name and host from cluster labels, which is
// formatted as follow: cluster_metadata:
// filter_metadata:
// istio:
// services:
// - host: a.default
// name: a
// namespace: default
// - host: b.default
// name: b
// namespace: default
// Multiple services could be added to a inbound cluster when they are bound
// to the same port. Currently we use the first service in the list (the
// oldest service) to get destination service information. Ideally client will
// forward the canonical host to the server side so that it could accurately
// identify the intended host.
if (getValue({"cluster_metadata", "filter_metadata", "istio", "services", "0", "name"},
&request_info->destination_service_name)) {
getValue({"cluster_metadata", "filter_metadata", "istio", "services", "0", "host"},
&request_info->destination_service_host);
} else {
// if cluster metadata cannot be found, fallback to destination service
// host. If host header fallback is enabled, this will be host header. If
// host header fallback is disabled, this will be unknown. This could happen
// if a request does not route to any cluster.
request_info->destination_service_name = request_info->destination_service_host;
}
}
} // namespace
void populateRequestInfo(bool outbound, bool use_host_header_fallback, RequestInfo* request_info) {
if (request_info->is_populated) {
return;
}
request_info->is_populated = true;
getValue({"cluster_name"}, &request_info->upstream_cluster);
getValue({"route_name"}, &request_info->route_name);
// Fill in request info.
// Get destination service name and host based on cluster name and host
// header.
populateDestinationService(outbound, use_host_header_fallback, request_info);
uint64_t destination_port = 0;
if (outbound) {
getValue({"upstream", "port"}, &destination_port);
getValue({"upstream", "uri_san_peer_certificate"}, &request_info->destination_principal);
getValue({"upstream", "uri_san_local_certificate"}, &request_info->source_principal);
} else {
getValue({"destination", "port"}, &destination_port);
bool mtls = false;
if (getValue({"connection", "mtls"}, &mtls)) {
request_info->service_auth_policy =
mtls ? ::Wasm::Common::ServiceAuthenticationPolicy::MutualTLS
: ::Wasm::Common::ServiceAuthenticationPolicy::None;
}
getValue({"connection", "uri_san_local_certificate"}, &request_info->destination_principal);
getValue({"connection", "uri_san_peer_certificate"}, &request_info->source_principal);
}
request_info->destination_port = destination_port;
}
std::string_view AuthenticationPolicyString(ServiceAuthenticationPolicy policy) {
switch (policy) {
case ServiceAuthenticationPolicy::None:
return kNone;
case ServiceAuthenticationPolicy::MutualTLS:
return kMutualTLS;
default:
break;
}
return {};
}
std::string_view TCPConnectionStateString(TCPConnectionState state) {
switch (state) {
case TCPConnectionState::Open:
return kOpen;
case TCPConnectionState::Connected:
return kConnected;
case TCPConnectionState::Close:
return kClose;
default:
break;
}
return {};
}
std::string_view ProtocolString(Protocol protocol) {
switch (protocol) {
case Protocol::TCP:
return kProtocolTCP;
case Protocol::HTTP:
return kProtocolHTTP;
case Protocol::GRPC:
return kProtocolGRPC;
default:
break;
}
return {};
}
// Retrieves the traffic direction from the configuration context.
TrafficDirection getTrafficDirection() {
int64_t direction;
if (getValue({"listener_direction"}, &direction)) {
return static_cast<TrafficDirection>(direction);
}
return TrafficDirection::Unspecified;
}
flatbuffers::DetachedBuffer extractEmptyNodeFlatBuffer() {
flatbuffers::FlatBufferBuilder fbb;
FlatNodeBuilder node(fbb);
auto data = node.Finish();
fbb.Finish(data);
return fbb.Release();
}
flatbuffers::DetachedBuffer extractLocalNodeFlatBuffer() {
flatbuffers::FlatBufferBuilder fbb;
flatbuffers::Offset<flatbuffers::String> name, namespace_, owner, workload_name, istio_version,
mesh_id, cluster_id;
std::vector<flatbuffers::Offset<KeyVal>> labels, platform_metadata;
std::vector<flatbuffers::Offset<flatbuffers::String>> app_containers;
std::vector<flatbuffers::Offset<flatbuffers::String>> ip_addrs;
std::string value;
if (getValue({"node", "metadata", "NAME"}, &value)) {
name = fbb.CreateString(value);
}
if (getValue({"node", "metadata", "NAMESPACE"}, &value)) {
namespace_ = fbb.CreateString(value);
}
if (getValue({"node", "metadata", "OWNER"}, &value)) {
owner = fbb.CreateString(value);
}
if (getValue({"node", "metadata", "WORKLOAD_NAME"}, &value)) {
workload_name = fbb.CreateString(value);
}
if (getValue({"node", "metadata", "ISTIO_VERSION"}, &value)) {
istio_version = fbb.CreateString(value);
}
if (getValue({"node", "metadata", "MESH_ID"}, &value)) {
mesh_id = fbb.CreateString(value);
}
if (getValue({"node", "metadata", "CLUSTER_ID"}, &value)) {
cluster_id = fbb.CreateString(value);
}
{
auto buf = getProperty({"node", "metadata", "LABELS"});
if (buf.has_value()) {
for (const auto& [key, val] : buf.value()->pairs()) {
labels.push_back(CreateKeyVal(fbb, fbb.CreateString(key), fbb.CreateString(val)));
}
}
}
{
auto buf = getProperty({"node", "metadata", "PLATFORM_METADATA"});
if (buf.has_value()) {
for (const auto& [key, val] : buf.value()->pairs()) {
platform_metadata.push_back(
CreateKeyVal(fbb, fbb.CreateString(key), fbb.CreateString(val)));
}
}
}
if (getValue({"node", "metadata", "APP_CONTAINERS"}, &value)) {
std::vector<absl::string_view> containers = absl::StrSplit(value, ',');
for (const auto& container : containers) {
app_containers.push_back(fbb.CreateString(toStdStringView(container)));
}
}
if (getValue({"node", "metadata", "INSTANCE_IPS"}, &value)) {
std::vector<absl::string_view> ips = absl::StrSplit(value, ',');
for (const auto& ip : ips) {
ip_addrs.push_back(fbb.CreateString(toStdStringView(ip)));
}
}
auto labels_offset = fbb.CreateVectorOfSortedTables(&labels);
auto platform_metadata_offset = fbb.CreateVectorOfSortedTables(&platform_metadata);
auto app_containers_offset = fbb.CreateVector(app_containers);
auto ip_addrs_offset = fbb.CreateVector(ip_addrs);
FlatNodeBuilder node(fbb);
node.add_name(name);
node.add_namespace_(namespace_);
node.add_owner(owner);
node.add_workload_name(workload_name);
node.add_istio_version(istio_version);
node.add_mesh_id(mesh_id);
node.add_cluster_id(cluster_id);
node.add_labels(labels_offset);
node.add_platform_metadata(platform_metadata_offset);
node.add_app_containers(app_containers_offset);
node.add_instance_ips(ip_addrs_offset);
auto data = node.Finish();
fbb.Finish(data);
return fbb.Release();
}
namespace {
bool extractPeerMetadataFromUpstreamMetadata(const std::string& metadata_type,
flatbuffers::FlatBufferBuilder& fbb) {
std::string endpoint_labels;
if (!getValue({metadata_type, "filter_metadata", "istio", "workload"}, &endpoint_labels)) {
return false;
}
std::vector<absl::string_view> parts = absl::StrSplit(endpoint_labels, ';');
// workload label should semicolon separated four parts string:
// workload_name;namespace;canonical_service;canonical_revision;cluster_id.
if (parts.size() < 5) {
return false;
}
flatbuffers::Offset<flatbuffers::String> workload_name, namespace_, cluster_id;
std::vector<flatbuffers::Offset<KeyVal>> labels;
workload_name = fbb.CreateString(toStdStringView(parts[0]));
namespace_ = fbb.CreateString(toStdStringView(parts[1]));
if (!parts[2].empty()) {
labels.push_back(CreateKeyVal(fbb, fbb.CreateString(kCanonicalServiceLabelName),
fbb.CreateString(toStdStringView(parts[2]))));
}
if (!parts[3].empty()) {
labels.push_back(CreateKeyVal(fbb, fbb.CreateString(kCanonicalServiceRevisionLabelName),
fbb.CreateString(toStdStringView(parts[3]))));
}
if (parts.size() >= 5) {
// In case newer proxy runs with old control plane, only extract cluster
// name if there are the fifth part.
cluster_id = fbb.CreateString(toStdStringView(parts[4]));
}
auto labels_offset = fbb.CreateVectorOfSortedTables(&labels);
FlatNodeBuilder node(fbb);
node.add_workload_name(workload_name);
node.add_namespace_(namespace_);
if (!cluster_id.IsNull()) {
node.add_cluster_id(cluster_id);
}
node.add_labels(labels_offset);
auto data = node.Finish();
fbb.Finish(data);
return true;
}
} // namespace
bool extractPeerMetadataFromUpstreamClusterMetadata(flatbuffers::FlatBufferBuilder& fbb) {
return extractPeerMetadataFromUpstreamMetadata("cluster_metadata", fbb);
}
bool extractPeerMetadataFromUpstreamHostMetadata(flatbuffers::FlatBufferBuilder& fbb) {
return extractPeerMetadataFromUpstreamMetadata("upstream_host_metadata", fbb);
}
PeerNodeInfo::PeerNodeInfo(const std::string_view peer_metadata_id_key,
const std::string_view peer_metadata_key) {
// Attempt to read from filter_state first.
found_ = getValue({peer_metadata_id_key}, &peer_id_);
if (found_) {
if (getValue({peer_metadata_key}, &peer_node_)) {
return;
}
}
// Sentinel value is preserved as ID to implement maybeWaiting.
found_ = false;
if (getValue({kMetadataNotFoundValue}, &peer_id_)) {
peer_id_ = kMetadataNotFoundValue;
}
// Downstream peer metadata will never be in localhost endpoint. Skip
// looking for it.
if (peer_metadata_id_key == kDownstreamMetadataIdKey) {
fallback_peer_node_ = extractEmptyNodeFlatBuffer();
return;
}
// Construct a fallback peer node metadata based on endpoint labels if it is
// not in filter state. This may happen before metadata is received as well.
flatbuffers::FlatBufferBuilder fbb;
if (extractPeerMetadataFromUpstreamHostMetadata(fbb)) {
fallback_peer_node_ = fbb.Release();
} else {
fallback_peer_node_ = extractEmptyNodeFlatBuffer();
}
}
const ::Wasm::Common::FlatNode& PeerNodeInfo::get() const {
if (found_) {
return *flatbuffers::GetRoot<::Wasm::Common::FlatNode>(
reinterpret_cast<const uint8_t*>(peer_node_.data()));
}
return *flatbuffers::GetRoot<::Wasm::Common::FlatNode>(fallback_peer_node_.data());
}
// Host header is used if use_host_header_fallback==true.
void populateHTTPRequestInfo(bool outbound, bool use_host_header_fallback,
RequestInfo* request_info) {
populateRequestProtocol(request_info);
getValue({"request", "url_path"}, &request_info->url_path);
populateRequestInfo(outbound, use_host_header_fallback, request_info);
int64_t response_code = 0;
if (getValue({"response", "code"}, &response_code)) {
request_info->response_code = response_code;
}
uint64_t response_flags = 0;
if (getValue({"response", "flags"}, &response_flags)) {
request_info->response_flag = parseResponseFlag(response_flags);
}
if (request_info->request_protocol == Protocol::GRPC) {
int64_t grpc_status_code = 2;
getValue({"response", "grpc_status"}, &grpc_status_code);
request_info->grpc_status = grpc_status_code;
populateGRPCInfo(request_info);
}
std::string operation_id;
request_info->request_operation =
getValue({::Wasm::Common::kRequestOperationKey}, &operation_id)
? operation_id
: getHeaderMapValue(WasmHeaderMapType::RequestHeaders, kMethodHeaderKey)->toString();
getValue({"request", "time"}, &request_info->start_time);
getValue({"request", "duration"}, &request_info->duration);
getValue({"request", "total_size"}, &request_info->request_size);
getValue({"response", "total_size"}, &request_info->response_size);
}
std::string_view nodeInfoSchema() {
return std::string_view(reinterpret_cast<const char*>(FlatNodeBinarySchema::data()),
FlatNodeBinarySchema::size());
}
void populateExtendedHTTPRequestInfo(RequestInfo* request_info) {
populateExtendedRequestInfo(request_info);
if (getValue({"request", "referer"}, &request_info->referer)) {
sanitizeBytes(&request_info->referer);
}
if (getValue({"request", "useragent"}, &request_info->user_agent)) {
sanitizeBytes(&request_info->user_agent);
}
if (getValue({"request", "id"}, &request_info->request_id)) {
sanitizeBytes(&request_info->request_id);
}
std::string trace_sampled;
if (getValue({"request", "headers", "x-b3-sampled"}, &trace_sampled) && trace_sampled == "1") {
if (getValue({"request", "headers", "x-b3-traceid"}, &request_info->b3_trace_id)) {
sanitizeBytes(&request_info->b3_trace_id);
}
if (getValue({"request", "headers", "x-b3-spanid"}, &request_info->b3_span_id)) {
sanitizeBytes(&request_info->b3_span_id);
}
request_info->b3_trace_sampled = true;
}
getValue({"request", "path"}, &request_info->path);
getValue({"request", "host"}, &request_info->url_host);
getValue({"request", "scheme"}, &request_info->url_scheme);
std::string response_details;
getValue({"response", "code_details"}, &response_details);
if (!response_details.empty()) {
request_info->response_details = response_details;
}
}
void populateExtendedRequestInfo(RequestInfo* request_info) {
getValue({"source", "address"}, &request_info->source_address);
getValue({"destination", "address"}, &request_info->destination_address);
getValue({"source", "port"}, &request_info->source_port);
getValue({"connection_id"}, &request_info->connection_id);
getValue({"upstream", "address"}, &request_info->upstream_host);
getValue({"connection", "requested_server_name"}, &request_info->requested_server_name);
auto envoy_original_path =
getHeaderMapValue(WasmHeaderMapType::RequestHeaders, kEnvoyOriginalPathKey);
request_info->x_envoy_original_path = envoy_original_path ? envoy_original_path->toString() : "";
sanitizeBytes(&request_info->x_envoy_original_path);
auto envoy_original_dst_host =
getHeaderMapValue(WasmHeaderMapType::RequestHeaders, kEnvoyOriginalDstHostKey);
request_info->x_envoy_original_dst_host =
envoy_original_dst_host ? envoy_original_dst_host->toString() : "";
sanitizeBytes(&request_info->x_envoy_original_dst_host);
getValue({"upstream", "transport_failure_reason"},
&request_info->upstream_transport_failure_reason);
std::string response_details;
getValue({"connection", "termination_details"}, &response_details);
if (!response_details.empty()) {
request_info->response_details = response_details;
}
}
void populateTCPRequestInfo(bool outbound, RequestInfo* request_info) {
// host_header_fallback is for HTTP/gRPC only.
populateRequestInfo(outbound, false, request_info);
uint64_t response_flags = 0;
if (getValue({"response", "flags"}, &response_flags)) {
request_info->response_flag = parseResponseFlag(response_flags);
}
request_info->request_protocol = Protocol::TCP;
}
void populateRequestProtocol(RequestInfo* request_info) {
if (kGrpcContentTypes.count(
getHeaderMapValue(WasmHeaderMapType::RequestHeaders, kContentTypeHeaderKey)
->toString()) != 0) {
request_info->request_protocol = Protocol::GRPC;
} else {
// TODO Add http/1.1, http/1.0, http/2 in a separate attribute.
// http|grpc classification is compatible with Mixerclient
request_info->request_protocol = Protocol::HTTP;
}
}
bool populateGRPCInfo(RequestInfo* request_info) {
std::string value;
if (!getValue({"filter_state", GrpcStatsName}, &value)) {
return false;
}
// The expected byte serialization of grpc_stats filter is "x,y" where "x"
// is the request message count and "y" is the response message count.
std::vector<absl::string_view> parts = absl::StrSplit(value, ',');
if (parts.size() == 2) {
return absl::SimpleAtoi(parts[0], &request_info->request_message_count) &&
absl::SimpleAtoi(parts[1], &request_info->response_message_count);
}
return false;
}
bool getAuditPolicy() {
bool shouldAudit = false;
if (!getValue<bool>({"metadata", "filter_metadata", "envoy.common", "access_log_hint"},
&shouldAudit)) {
return false;
}
return shouldAudit;
}
bool sanitizeBytes(std::string* buf) {
char* start = buf->data();
const char* const end = start + buf->length();
bool modified = false;
while (start < end) {
char* s = start;
if (flatbuffers::FromUTF8(const_cast<const char**>(&s)) < 0) {
*start = ' ';
start += 1;
modified = true;
} else {
start = s;
}
}
return modified;
}
// Used for `destination_service` fallback. Unlike elsewhere when that fallback
// to workload name, this falls back to "unknown" when the canonical name label
// is not found. This preserves the existing behavior for `destination_service`
// labeling. Using a workload name as a service name could be potentially
// problematic.
std::string getServiceNameFallback() {
auto buf = getProperty({"node", "metadata", "LABELS"});
if (buf.has_value()) {
for (const auto& [key, val] : buf.value()->pairs())
if (key == ::Wasm::Common::kCanonicalServiceLabelName.data()) {
return std::string(val);
}
}
return "unknown";
}
} // namespace Common
} // namespace Wasm

302
extensions/common/context.h Normal file
View File

@ -0,0 +1,302 @@
/* 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 <set>
#include "extensions/common/node_info_generated.h"
#include "flatbuffers/flatbuffers.h"
namespace Wasm {
namespace Common {
// Node metadata
constexpr std::string_view WholeNodeKey = ".";
constexpr std::string_view kUpstreamMetadataIdKey = "upstream_peer_id";
constexpr std::string_view kUpstreamMetadataKey = "upstream_peer";
constexpr std::string_view kDownstreamMetadataIdKey = "downstream_peer_id";
constexpr std::string_view kDownstreamMetadataKey = "downstream_peer";
// 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";
constexpr std::string_view kAccessLogPolicyKey = "istio.access_log_policy";
constexpr std::string_view kRequestOperationKey = "istio_operationId";
// Header keys
constexpr std::string_view kAuthorityHeaderKey = ":authority";
constexpr std::string_view kMethodHeaderKey = ":method";
constexpr std::string_view kContentTypeHeaderKey = "content-type";
constexpr std::string_view kEnvoyOriginalDstHostKey = "x-envoy-original-dst-host";
constexpr std::string_view kEnvoyOriginalPathKey = "x-envoy-original-path";
constexpr std::string_view kProtocolHTTP = "http";
constexpr std::string_view kProtocolGRPC = "grpc";
constexpr std::string_view kProtocolTCP = "tcp";
constexpr std::string_view kCanonicalServiceLabelName = "service.istio.io/canonical-name";
constexpr std::string_view kCanonicalServiceRevisionLabelName =
"service.istio.io/canonical-revision";
constexpr std::string_view kLatest = "latest";
const std::set<std::string> kGrpcContentTypes{"application/grpc", "application/grpc+proto",
"application/grpc+json"};
enum class ServiceAuthenticationPolicy : uint8_t {
Unspecified = 0,
None = 1,
MutualTLS = 2,
};
enum class TCPConnectionState : uint8_t {
Unspecified = 0,
Open = 1,
Connected = 2,
Close = 3,
};
enum class Protocol : uint32_t {
Unspecified = 0x0,
TCP = 0x1,
HTTP = 0x2,
GRPC = 0x4,
};
constexpr std::string_view kMutualTLS = "MUTUAL_TLS";
constexpr std::string_view kNone = "NONE";
constexpr std::string_view kOpen = "OPEN";
constexpr std::string_view kConnected = "CONNECTED";
constexpr std::string_view kClose = "CLOSE";
std::string_view AuthenticationPolicyString(ServiceAuthenticationPolicy policy);
std::string_view TCPConnectionStateString(TCPConnectionState state);
std::string_view ProtocolString(Protocol protocol);
// RequestInfo represents the information collected from filter stream
// callbacks. This is used to fill metrics and logs.
struct RequestInfo {
// Start timestamp in nanoseconds.
int64_t start_time;
// The total duration of the request in nanoseconds.
int64_t duration;
// Request total size in bytes, include header, body, and trailer.
int64_t request_size = 0;
// Response total size in bytes, include header, body, and trailer.
int64_t response_size = 0;
// Destination port that the request targets.
uint32_t destination_port = 0;
// Source port of the client.
uint64_t source_port = 0;
// Protocol used the request (HTTP/1.1, gRPC, etc).
Protocol request_protocol = Protocol::Unspecified;
// Response code of the request.
uint32_t response_code = 0;
// gRPC status code for the request.
uint32_t grpc_status = 2;
// Response flag giving additional information - NR, UAEX etc.
std::string response_flag;
// Host name of destination service.
std::string destination_service_host;
// Short name of destination service.
std::string destination_service_name;
// Operation of the request, i.e. HTTP method or gRPC API method.
std::string request_operation;
std::string upstream_transport_failure_reason;
// Service authentication policy (NONE, MUTUAL_TLS)
ServiceAuthenticationPolicy service_auth_policy = ServiceAuthenticationPolicy::Unspecified;
// Principal of source and destination workload extracted from TLS
// certificate.
std::string source_principal;
std::string destination_principal;
// Connection id of the TCP connection.
uint64_t connection_id;
// The following fields will only be populated by calling
// populateExtendedHTTPRequestInfo.
std::string source_address;
std::string destination_address;
std::string response_details;
// Additional fields for access log.
std::string route_name;
std::string upstream_host;
std::string upstream_cluster;
std::string requested_server_name;
std::string x_envoy_original_path;
std::string x_envoy_original_dst_host;
// Important Headers.
std::string referer;
std::string user_agent;
std::string request_id;
std::string b3_trace_id;
std::string b3_span_id;
bool b3_trace_sampled = false;
// HTTP URL related attributes.
// The path portion of the URL including the query string.
std::string path;
// The path portion of the URL without the query string.
std::string url_path;
std::string url_host;
std::string url_scheme;
// TCP variables.
uint8_t tcp_connections_opened = 0;
uint8_t tcp_connections_closed = 0;
uint64_t tcp_sent_bytes = 0;
uint64_t tcp_received_bytes = 0;
uint64_t tcp_total_sent_bytes = 0;
uint64_t tcp_total_received_bytes = 0;
TCPConnectionState tcp_connection_state = TCPConnectionState::Unspecified;
bool is_populated = false;
bool log_sampled = false;
// gRPC variables.
uint64_t request_message_count = 0;
uint64_t response_message_count = 0;
uint64_t last_request_message_count = 0;
uint64_t last_response_message_count = 0;
};
// RequestContext contains all the information available in the request.
// Some or all part may be populated depending on need.
struct RequestContext {
const bool outbound;
const Common::RequestInfo& request;
};
// TrafficDirection is a mirror of envoy xDS traffic direction.
enum class TrafficDirection : int64_t {
Unspecified = 0,
Inbound = 1,
Outbound = 2,
};
// Retrieves the traffic direction from the configuration context.
TrafficDirection getTrafficDirection();
// Convenience routine to create an empty node flatbuffer.
flatbuffers::DetachedBuffer extractEmptyNodeFlatBuffer();
// Extract local node metadata into a flatbuffer. Detached buffer owns the
// underlying heap-allocated memory. Note that std::string is inappropriate here
// because its memory is inlined for short strings and causes a misaligned
// address access.
flatbuffers::DetachedBuffer extractLocalNodeFlatBuffer();
// Extract upstream peer metadata from upstream host metadata.
// Returns true if the metadata is found in the upstream host metadata.
bool extractPeerMetadataFromUpstreamHostMetadata(flatbuffers::FlatBufferBuilder& fbb);
// Extract upstream peer metadata from upstream cluster metadata.
// Returns true if the metadata is found in the upstream cluster metadata.
bool extractPeerMetadataFromUpstreamClusterMetadata(flatbuffers::FlatBufferBuilder& fbb);
// Returns flatbuffer schema for node info.
std::string_view nodeInfoSchema();
class PeerNodeInfo {
public:
explicit PeerNodeInfo(const std::string_view peer_metadata_id_key,
const std::string_view peer_metadata_key);
PeerNodeInfo() = delete;
const ::Wasm::Common::FlatNode& get() const;
const std::string& id() const { return peer_id_; }
// Found indicates whether both ID and metadata is available.
bool found() const { return found_; }
// Maybe waiting indicates that the metadata is not found but may arrive
// later.
bool maybeWaiting() const {
return !found_ && peer_id_ != ::Wasm::Common::kMetadataNotFoundValue;
}
private:
bool found_;
std::string peer_id_;
std::string peer_node_;
flatbuffers::DetachedBuffer fallback_peer_node_;
};
// Populate shared information between all protocols.
// Requires that the connections are established both downstrean and upstream.
// Caches computation using is_populated field.
void populateRequestInfo(bool outbound, bool use_host_header_fallback, RequestInfo* request_info);
// populateHTTPRequestInfo populates the RequestInfo struct. It needs access to
// the request context.
void populateHTTPRequestInfo(bool outbound, bool use_host_header, RequestInfo* request_info);
// populateExtendedHTTPRequestInfo populates the extra fields in RequestInfo
// struct, includes trace headers, request id headers, and url.
void populateExtendedHTTPRequestInfo(RequestInfo* request_info);
// populateExtendedRequestInfo populates the extra fields in RequestInfo
// source address, destination address.
void populateExtendedRequestInfo(RequestInfo* request_info);
// populateTCPRequestInfo populates the RequestInfo struct. It needs access to
// the request context.
void populateTCPRequestInfo(bool outbound, RequestInfo* request_info);
// Detect HTTP and gRPC request protocols.
void populateRequestProtocol(RequestInfo* request_info);
// populateGRPCInfo fills gRPC-related information, such as message counts.
// Returns true if all information is filled.
bool populateGRPCInfo(RequestInfo* request_info);
// Read value of 'access_log_hint' key from envoy dynamic metadata which
// determines whether to audit a request or not.
bool getAuditPolicy();
// Returns a string view stored in a flatbuffers string.
static inline std::string_view GetFromFbStringView(const flatbuffers::String* str) {
return str ? std::string_view(str->c_str(), str->size()) : std::string_view();
}
// Sanitizes a possible UTF-8 byte buffer to a UTF-8 string.
// Invalid byte sequences are replaced by spaces.
// Returns true if the string was modified.
bool sanitizeBytes(std::string* buf);
std::string getServiceNameFallback();
} // namespace Common
} // namespace Wasm

View File

@ -0,0 +1,103 @@
/* 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 <set>
#include "absl/container/flat_hash_map.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_replace.h"
namespace Wasm {
namespace Common {
#define STD_ISTIO_DIMENSIONS(FIELD_FUNC) \
FIELD_FUNC(downstream_ip) \
FIELD_FUNC(reporter) \
FIELD_FUNC(source_workload) \
FIELD_FUNC(source_workload_namespace) \
FIELD_FUNC(source_principal) \
FIELD_FUNC(source_app) \
FIELD_FUNC(source_version) \
FIELD_FUNC(source_canonical_service) \
FIELD_FUNC(source_canonical_revision) \
FIELD_FUNC(destination_workload) \
FIELD_FUNC(destination_workload_namespace) \
FIELD_FUNC(destination_principal) \
FIELD_FUNC(destination_app) \
FIELD_FUNC(destination_version) \
FIELD_FUNC(destination_service) \
FIELD_FUNC(destination_service_name) \
FIELD_FUNC(destination_service_namespace) \
FIELD_FUNC(destination_canonical_service) \
FIELD_FUNC(destination_canonical_revision) \
FIELD_FUNC(destination_port) \
FIELD_FUNC(request_protocol) \
FIELD_FUNC(response_code) \
FIELD_FUNC(grpc_response_status) \
FIELD_FUNC(response_flags) \
FIELD_FUNC(connection_security_policy)
// A structure that can hold multiple Istio dimensions(metadata variables).
// This could be use to key caches based on Istio dimensions for various
// filters.
// Note: This is supposed to be used with absl::flat_hash_map only.
// TODO: Add support for evaluating dynamic Istio dimensions.
struct IstioDimensions {
#define DEFINE_FIELD(name) std::string(name);
STD_ISTIO_DIMENSIONS(DEFINE_FIELD)
#undef DEFINE_FIELD
bool outbound = false;
#define SET_FIELD(name) \
IstioDimensions& set_##name(std::string value) { \
name = value; \
return *this; \
}
STD_ISTIO_DIMENSIONS(SET_FIELD)
#undef SET_FIELD
IstioDimensions& set_outbound(bool value) {
outbound = value;
return *this;
}
std::string to_string() const {
#define TO_STRING(name) "\"", #name, "\":\"", name, "\" ,",
return absl::StrCat("{" STD_ISTIO_DIMENSIONS(TO_STRING) "\"outbound\": ", outbound, "}");
#undef TO_STRING
}
// This function is required to make IstioDimensions type hashable.
template <typename H> friend H AbslHashValue(H h, IstioDimensions d) {
#define TO_HASH_VALUE(name) , d.name
return H::combine(std::move(h) STD_ISTIO_DIMENSIONS(TO_HASH_VALUE), d.outbound);
#undef TO_HASH_VALUE
}
// This function is required to make IstioDimensions type hashable.
friend bool operator==(const IstioDimensions& lhs, const IstioDimensions& rhs) {
return (
#define COMPARE(name) lhs.name == rhs.name&&
STD_ISTIO_DIMENSIONS(COMPARE) lhs.outbound == rhs.outbound);
#undef COMPARE
}
};
} // namespace Common
} // namespace Wasm

View File

@ -0,0 +1,57 @@
/* 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 "extensions/common/istio_dimensions.h"
#include "absl/hash/hash_testing.h"
#include "gtest/gtest.h"
namespace Wasm {
namespace Common {
namespace {
TEST(WasmCommonIstioDimensionsTest, VerifyHashing) {
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
IstioDimensions{},
IstioDimensions().set_request_protocol("wrpc"),
IstioDimensions().set_request_protocol("grpc").set_response_code("200"),
IstioDimensions().set_request_protocol("grpc").set_response_code("400"),
IstioDimensions().set_source_app("app_source").set_request_protocol("grpc"),
IstioDimensions()
.set_source_app("app_source")
.set_source_version("v2")
.set_request_protocol("grpc"),
IstioDimensions()
.set_source_app("app_source")
.set_source_version("v2")
.set_request_protocol("grpc")
.set_outbound(true),
IstioDimensions()
.set_source_app("app_source")
.set_source_version("v2")
.set_request_protocol("grpc")
.set_outbound(true),
IstioDimensions()
.set_source_app("app_source")
.set_source_version("v2")
.set_request_protocol("grpc")
.set_grpc_response_status("12")
.set_outbound(true),
}));
}
} // namespace
} // namespace Common
} // namespace Wasm

View File

@ -14,26 +14,24 @@
#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"
#include "flatbuffers/flatbuffers.h"
#include "source/common/common/hash.h"
namespace Istio {
namespace Common {
namespace {
static absl::flat_hash_map<absl::string_view, BaggageToken> ALL_BAGGAGE_TOKENS = {
{NamespaceNameToken, BaggageToken::NamespaceName},
{ClusterNameToken, BaggageToken::ClusterName},
{ServiceNameToken, BaggageToken::ServiceName},
{ServiceVersionToken, BaggageToken::ServiceVersion},
{PodNameToken, BaggageToken::PodName},
{DeploymentNameToken, BaggageToken::DeploymentName},
{JobNameToken, BaggageToken::JobName},
{CronJobNameToken, BaggageToken::CronJobName},
{AppNameToken, BaggageToken::AppName},
{AppVersionToken, BaggageToken::AppVersion},
{WorkloadNameToken, BaggageToken::WorkloadName},
{WorkloadTypeToken, BaggageToken::WorkloadType},
{InstanceNameToken, BaggageToken::InstanceName},
};
static absl::flat_hash_map<absl::string_view, WorkloadType> ALL_WORKLOAD_TOKENS = {
@ -43,283 +41,9 @@ static absl::flat_hash_map<absl::string_view, WorkloadType> ALL_WORKLOAD_TOKENS
{CronJobSuffix, WorkloadType::CronJob},
};
absl::optional<absl::string_view> 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:
return {};
}
}
} // namespace
Envoy::ProtobufTypes::MessagePtr WorkloadMetadataObject::serializeAsProto() const {
auto message = std::make_unique<Envoy::ProtobufWkt::Struct>();
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<std::pair<absl::string_view, absl::string_view>>
WorkloadMetadataObject::serializeAsPairs() const {
std::vector<std::pair<absl::string_view, absl::string_view>> 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<std::string> WorkloadMetadataObject::serializeAsString() const {
const auto parts = serializeAsPairs();
return absl::StrJoin(parts, ",", absl::PairFormatter("="));
}
absl::optional<uint64_t> WorkloadMetadataObject::hash() const {
return Envoy::HashUtil::xxHash64(*serializeAsString());
}
absl::optional<std::string> 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<WorkloadMetadataObject>
convertStructToWorkloadMetadata(const google::protobuf::Struct& metadata) {
return convertStructToWorkloadMetadata(metadata, {});
}
std::unique_ptr<WorkloadMetadataObject>
convertStructToWorkloadMetadata(const google::protobuf::Struct& metadata,
const absl::flat_hash_set<std::string>& additional_labels) {
absl::string_view instance, namespace_name, owner, workload, cluster, canonical_name,
canonical_revision, app_name, app_version;
std::vector<std::pair<std::string, std::string>> 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<WorkloadMetadataObject>(instance, cluster, namespace_name, workload,
canonical_name, canonical_revision, app_name,
app_version, parseOwner(owner, workload), "");
obj->setLabels(labels);
return obj;
}
absl::optional<WorkloadMetadataObject>
convertEndpointMetadata(const std::string& endpoint_encoding) {
std::vector<absl::string_view> parts = absl::StrSplit(endpoint_encoding, ';');
if (parts.size() < 5) {
return {};
}
return absl::make_optional<WorkloadMetadataObject>("", 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;
}
case BaggageToken::InstanceName:
return instance_name_;
}
}
return {};
}
std::unique_ptr<WorkloadMetadataObject> convertBaggageToWorkloadMetadata(absl::string_view data) {
WorkloadMetadataObject WorkloadMetadataObject::fromBaggage(absl::string_view baggage_header_value) {
// TODO: check for well-formed-ness of the baggage string: duplication,
// inconsistency
absl::string_view instance;
absl::string_view cluster;
absl::string_view workload;
@ -328,10 +52,11 @@ std::unique_ptr<WorkloadMetadataObject> convertBaggageToWorkloadMetadata(absl::s
absl::string_view canonical_revision;
absl::string_view app_name;
absl::string_view app_version;
WorkloadType workload_type = WorkloadType::Unknown;
std::vector<absl::string_view> properties = absl::StrSplit(data, ',');
WorkloadType workload_type = WorkloadType::Pod;
std::vector<absl::string_view> properties = absl::StrSplit(baggage_header_value, ',');
for (absl::string_view property : properties) {
std::pair<absl::string_view, absl::string_view> parts = absl::StrSplit(property, '=');
std::pair<absl::string_view, absl::string_view> parts = absl::StrSplit(property, "=");
const auto it = ALL_BAGGAGE_TOKENS.find(parts.first);
if (it != ALL_BAGGAGE_TOKENS.end()) {
switch (it->second) {
@ -347,27 +72,240 @@ std::unique_ptr<WorkloadMetadataObject> convertBaggageToWorkloadMetadata(absl::s
case BaggageToken::ServiceVersion:
canonical_revision = parts.second;
break;
case BaggageToken::PodName:
workload_type = WorkloadType::Pod;
instance = parts.second;
workload = parts.second;
break;
case BaggageToken::DeploymentName:
workload_type = WorkloadType::Deployment;
workload = parts.second;
break;
case BaggageToken::JobName:
workload_type = WorkloadType::Job;
instance = parts.second;
workload = parts.second;
break;
case BaggageToken::CronJobName:
workload_type = WorkloadType::CronJob;
workload = 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<WorkloadMetadataObject>(instance, cluster, namespace_name, workload,
canonical_name, canonical_revision, app_name,
app_version, workload_type, "");
return WorkloadMetadataObject(instance, cluster, namespace_name, workload, canonical_name,
canonical_revision, app_name, app_version, workload_type);
}
std::string WorkloadMetadataObject::baggage() const {
absl::string_view workload_type = PodSuffix;
switch (workload_type_) {
case WorkloadType::Deployment:
workload_type = DeploymentSuffix;
break;
case WorkloadType::CronJob:
workload_type = CronJobSuffix;
break;
case WorkloadType::Job:
workload_type = JobSuffix;
break;
case WorkloadType::Pod:
workload_type = PodSuffix;
break;
default:
break;
}
std::vector<absl::string_view> parts;
parts.push_back("k8s.");
parts.push_back(workload_type);
parts.push_back(".name=");
parts.push_back(workload_name_);
if (!cluster_name_.empty()) {
parts.push_back(",");
parts.push_back(ClusterNameToken);
parts.push_back("=");
parts.push_back(cluster_name_);
}
if (!namespace_name_.empty()) {
parts.push_back(",");
parts.push_back(NamespaceNameToken);
parts.push_back("=");
parts.push_back(namespace_name_);
}
if (!canonical_name_.empty()) {
parts.push_back(",");
parts.push_back(ServiceNameToken);
parts.push_back("=");
parts.push_back(canonical_name_);
}
if (!canonical_revision_.empty()) {
parts.push_back(",");
parts.push_back(ServiceVersionToken);
parts.push_back("=");
parts.push_back(canonical_revision_);
}
if (!app_name_.empty()) {
parts.push_back(",");
parts.push_back(AppNameToken);
parts.push_back("=");
parts.push_back(app_name_);
}
if (!app_version_.empty()) {
parts.push_back(",");
parts.push_back(AppVersionToken);
parts.push_back("=");
parts.push_back(app_version_);
}
return absl::StrJoin(parts, "");
}
absl::optional<uint64_t> WorkloadMetadataObject::hash() const {
return Envoy::HashUtil::xxHash64(absl::StrCat(instance_name_, "/", namespace_name_));
}
namespace {
// Returns a string view stored in a flatbuffers string.
absl::string_view toAbslStringView(const flatbuffers::String* str) {
return str ? absl::string_view(str->c_str(), str->size()) : absl::string_view();
}
std::string_view toStdStringView(absl::string_view view) {
return std::string_view(view.data(), view.size());
}
} // namespace
std::string convertWorkloadMetadataToFlatNode(const WorkloadMetadataObject& obj) {
flatbuffers::FlatBufferBuilder fbb;
flatbuffers::Offset<flatbuffers::String> name, cluster, namespace_, workload_name, owner;
std::vector<flatbuffers::Offset<Wasm::Common::KeyVal>> labels;
name = fbb.CreateString(toStdStringView(obj.instance_name_));
namespace_ = fbb.CreateString(toStdStringView(obj.namespace_name_));
cluster = fbb.CreateString(toStdStringView(obj.cluster_name_));
workload_name = fbb.CreateString(toStdStringView(obj.workload_name_));
switch (obj.workload_type_) {
case WorkloadType::Deployment:
owner = fbb.CreateString(absl::StrCat(OwnerPrefix, obj.namespace_name_, "/", DeploymentSuffix,
"s/", obj.workload_name_));
break;
case WorkloadType::Job:
owner = fbb.CreateString(
absl::StrCat(OwnerPrefix, obj.namespace_name_, "/", JobSuffix, "s/", obj.workload_name_));
break;
case WorkloadType::CronJob:
owner = fbb.CreateString(absl::StrCat(OwnerPrefix, obj.namespace_name_, "/", CronJobSuffix,
"s/", obj.workload_name_));
break;
case WorkloadType::Pod:
owner = fbb.CreateString(
absl::StrCat(OwnerPrefix, obj.namespace_name_, "/", PodSuffix, "s/", obj.workload_name_));
break;
}
labels.push_back(
Wasm::Common::CreateKeyVal(fbb, fbb.CreateString("service.istio.io/canonical-name"),
fbb.CreateString(toStdStringView(obj.canonical_name_))));
labels.push_back(
Wasm::Common::CreateKeyVal(fbb, fbb.CreateString("service.istio.io/canonical-revision"),
fbb.CreateString(toStdStringView(obj.canonical_revision_))));
labels.push_back(Wasm::Common::CreateKeyVal(fbb, fbb.CreateString("app"),
fbb.CreateString(toStdStringView(obj.app_name_))));
labels.push_back(Wasm::Common::CreateKeyVal(fbb, fbb.CreateString("version"),
fbb.CreateString(toStdStringView(obj.app_version_))));
auto labels_offset = fbb.CreateVectorOfSortedTables(&labels);
Wasm::Common::FlatNodeBuilder node(fbb);
node.add_name(name);
node.add_cluster_id(cluster);
node.add_namespace_(namespace_);
node.add_workload_name(workload_name);
node.add_owner(owner);
node.add_labels(labels_offset);
auto data = node.Finish();
fbb.Finish(data);
auto fb = fbb.Release();
return std::string(reinterpret_cast<const char*>(fb.data()), fb.size());
}
WorkloadMetadataObject convertFlatNodeToWorkloadMetadata(const Wasm::Common::FlatNode& node) {
const absl::string_view instance = toAbslStringView(node.name());
const absl::string_view cluster = toAbslStringView(node.cluster_id());
const absl::string_view workload = toAbslStringView(node.workload_name());
const absl::string_view namespace_name = toAbslStringView(node.namespace_());
const auto* labels = node.labels();
absl::string_view canonical_name;
absl::string_view canonical_revision;
absl::string_view app_name;
absl::string_view app_version;
if (labels) {
const auto* name_iter = labels->LookupByKey("service.istio.io/canonical-name");
const auto* name = name_iter ? name_iter->value() : nullptr;
canonical_name = toAbslStringView(name);
const auto* revision_iter = labels->LookupByKey("service.istio.io/canonical-revision");
const auto* revision = revision_iter ? revision_iter->value() : nullptr;
canonical_revision = toAbslStringView(revision);
const auto* app_iter = labels->LookupByKey("app");
const auto* app = app_iter ? app_iter->value() : nullptr;
app_name = toAbslStringView(app);
const auto* version_iter = labels->LookupByKey("version");
const auto* version = version_iter ? version_iter->value() : nullptr;
app_version = toAbslStringView(version);
}
WorkloadType workload_type = WorkloadType::Pod;
// Strip "s/workload_name" and check for workload type.
absl::string_view owner = toAbslStringView(node.owner());
if (owner.size() > workload.size() + 2) {
owner.remove_suffix(workload.size() + 2);
size_t last = owner.rfind('/');
if (last != absl::string_view::npos) {
const auto it = ALL_WORKLOAD_TOKENS.find(owner.substr(last + 1));
if (it != ALL_WORKLOAD_TOKENS.end()) {
switch (it->second) {
case WorkloadType::Deployment:
workload_type = WorkloadType::Deployment;
break;
case WorkloadType::CronJob:
workload_type = WorkloadType::CronJob;
break;
case WorkloadType::Job:
workload_type = WorkloadType::Job;
break;
case WorkloadType::Pod:
workload_type = WorkloadType::Pod;
break;
default:
break;
}
}
}
}
return WorkloadMetadataObject(instance, cluster, namespace_name, workload, canonical_name,
canonical_revision, app_name, app_version, workload_type);
}
absl::optional<WorkloadMetadataObject>
convertEndpointMetadata(const std::string& endpoint_encoding) {
std::vector<absl::string_view> parts = absl::StrSplit(endpoint_encoding, ';');
if (parts.size() < 5) {
return {};
}
// TODO: we cannot determine workload type from the encoding.
return absl::make_optional<WorkloadMetadataObject>("", parts[4], parts[1], parts[0], parts[2],
parts[3], "", "", WorkloadType::Pod);
}
} // namespace Common

View File

@ -14,34 +14,17 @@
#pragma once
#include "envoy/common/hashable.h"
#include "envoy/stream_info/filter_state.h"
#include "source/common/protobuf/protobuf.h"
#include "absl/strings/str_split.h"
#include "absl/types/optional.h"
#include "google/protobuf/struct.pb.h"
#include "envoy/common/hashable.h"
#include "envoy/ssl/connection.h"
#include "envoy/stream_info/filter_state.h"
#include "extensions/common/node_info_generated.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,
@ -49,7 +32,6 @@ enum class WorkloadType {
};
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";
@ -60,56 +42,48 @@ enum class BaggageToken {
ClusterName,
ServiceName,
ServiceVersion,
PodName,
DeploymentName,
JobName,
CronJobName,
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 NamespaceNameToken = "k8s.namespace.name";
constexpr absl::string_view ClusterNameToken = "k8s.cluster.name";
constexpr absl::string_view ServiceNameToken = "service.name";
constexpr absl::string_view ServiceVersionToken = "service.version";
constexpr absl::string_view PodNameToken = "k8s.pod.name";
constexpr absl::string_view DeploymentNameToken = "k8s.deployment.name";
constexpr absl::string_view JobNameToken = "k8s.job.name";
constexpr absl::string_view CronJobNameToken = "k8s.cronjob.name";
constexpr absl::string_view AppNameToken = "app.name";
constexpr absl::string_view AppVersionToken = "app.version";
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";
constexpr absl::string_view kSourceMetadataObjectKey = "ambient.source.workloadMetadata";
constexpr absl::string_view kSourceMetadataBaggageKey = "ambient.source.workloadMetadataBaggage";
constexpr absl::string_view kDestinationMetadataObjectKey = "ambient.destination.workloadMetadata";
class WorkloadMetadataObject : public Envoy::StreamInfo::FilterState::Object,
public Envoy::Hashable {
public:
struct WorkloadMetadataObject : public Envoy::StreamInfo::FilterState::Object,
public Envoy::Hashable {
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)
absl::string_view app_version, const WorkloadType workload_type)
: 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) {}
workload_type_(workload_type) {}
static WorkloadMetadataObject fromBaggage(absl::string_view baggage_header_value);
std::string baggage() const;
absl::optional<uint64_t> hash() const override;
Envoy::ProtobufTypes::MessagePtr serializeAsProto() const override;
std::vector<std::pair<absl::string_view, absl::string_view>> serializeAsPairs() const;
absl::optional<std::string> serializeAsString() const override;
absl::optional<std::string> 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<std::pair<std::string, std::string>> labels) { labels_ = labels; }
std::vector<std::pair<std::string, std::string>> getLabels() const { return labels_; }
absl::optional<std::string> serializeAsString() const override { return baggage(); }
const std::string instance_name_;
const std::string cluster_name_;
@ -120,26 +94,13 @@ public:
const std::string app_name_;
const std::string app_version_;
const WorkloadType workload_type_;
const std::string identity_;
std::vector<std::pair<std::string, std::string>> labels_;
};
// Parse string workload type.
WorkloadType fromSuffix(absl::string_view suffix);
// Convert metadata object to flatbuffer.
std::string convertWorkloadMetadataToFlatNode(const WorkloadMetadataObject& obj);
// 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<WorkloadMetadataObject>
convertStructToWorkloadMetadata(const google::protobuf::Struct& metadata);
std::unique_ptr<WorkloadMetadataObject>
convertStructToWorkloadMetadata(const google::protobuf::Struct& metadata,
const absl::flat_hash_set<std::string>& additional_labels);
// Convert flatbuffer to metadata object.
WorkloadMetadataObject convertFlatNodeToWorkloadMetadata(const Wasm::Common::FlatNode& node);
// Convert endpoint metadata string to a metadata object.
// Telemetry metadata is compressed into a semicolon separated string:
@ -149,10 +110,5 @@ convertStructToWorkloadMetadata(const google::protobuf::Struct& metadata,
absl::optional<WorkloadMetadataObject>
convertEndpointMetadata(const std::string& endpoint_encoding);
std::string serializeToStringDeterministic(const google::protobuf::Struct& metadata);
// Convert from baggage encoding.
std::unique_ptr<WorkloadMetadataObject> convertBaggageToWorkloadMetadata(absl::string_view data);
} // namespace Common
} // namespace Istio

View File

@ -14,156 +14,154 @@
#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, Hash) {
WorkloadMetadataObject obj1("foo-pod-12345", "my-cluster", "default", "foo", "foo", "latest",
"foo-app", "v1", WorkloadType::Deployment);
WorkloadMetadataObject obj2("foo-pod-12345", "my-cluster", "default", "bar", "baz", "first",
"foo-app", "v1", WorkloadType::Job);
EXPECT_EQ(obj1.hash().value(), obj2.hash().value());
}
TEST(WorkloadMetadataObjectTest, Baggage) {
WorkloadMetadataObject deploy("pod-foo-1234", "my-cluster", "default", "foo", "foo-service",
"v1alpha3", "", "", WorkloadType::Deployment, "");
"v1alpha3", "foo-app", "v1", WorkloadType::Deployment);
WorkloadMetadataObject pod("pod-foo-1234", "my-cluster", "default", "foo", "foo-service",
"v1alpha3", "", "", WorkloadType::Pod, "");
"v1alpha3", "foo-app", "v1", WorkloadType::Pod);
WorkloadMetadataObject cronjob("pod-foo-1234", "my-cluster", "default", "foo", "foo-service",
"v1alpha3", "foo-app", "v1", WorkloadType::CronJob, "");
"v1alpha3", "foo-app", "v1", WorkloadType::CronJob);
WorkloadMetadataObject job("pod-foo-1234", "my-cluster", "default", "foo", "foo-service",
"v1alpha3", "", "", WorkloadType::Job, "");
"v1alpha3", "foo-app", "v1", 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(deploy.baggage(), absl::StrCat("k8s.deployment.name=foo,k8s.cluster.name=my-cluster,",
"k8s.namespace.name=default,",
"service.name=foo-service,service.version=v1alpha3,",
"app.name=foo-app,app.version=v1"));
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(pod.baggage(), absl::StrCat("k8s.pod.name=foo,k8s.cluster.name=my-cluster,",
"k8s.namespace.name=default,",
"service.name=foo-service,service.version=v1alpha3,",
"app.name=foo-app,app.version=v1"));
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(cronjob.baggage(), absl::StrCat("k8s.cronjob.name=foo,k8s.cluster.name=my-cluster,",
"k8s.namespace.name=default,"
"service.name=foo-service,service.version=v1alpha3,",
"app.name=foo-app,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"));
EXPECT_EQ(job.baggage(), absl::StrCat("k8s.job.name=foo,k8s.cluster.name=my-cluster,",
"k8s.namespace.name=default,",
"service.name=foo-service,service.version=v1alpha3,",
"app.name=foo-app,app.version=v1"));
}
void checkStructConversion(const Envoy::StreamInfo::FilterState::Object& data) {
const auto& obj = dynamic_cast<const WorkloadMetadataObject&>(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());
void checkFlatNodeConversion(const WorkloadMetadataObject& obj) {
auto buffer = convertWorkloadMetadataToFlatNode(obj);
const auto& node = *flatbuffers::GetRoot<Wasm::Common::FlatNode>(buffer.data());
auto obj2 = convertFlatNodeToWorkloadMetadata(node);
EXPECT_EQ(obj2.baggage(), obj.baggage());
}
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<std::string> empty;
auto obj3 = convertStructToWorkloadMetadata(pb, empty);
EXPECT_EQ(obj3->getLabels().size(), 0);
}
TEST(WorkloadMetadataObjectTest, Conversion) {
TEST(WorkloadMetadataObjectTest, FromBaggage) {
{
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<absl::string_view>(r->getField("service")), "foo-service");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("revision")), "v1alpha3");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("type")), DeploymentSuffix);
EXPECT_EQ(absl::get<absl::string_view>(r->getField("workload")), "foo");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("name")), "");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("namespace")), "default");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("cluster")), "my-cluster");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("app")), "foo-app");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("version")), "latest");
checkStructConversion(*r);
auto obj = WorkloadMetadataObject::fromBaggage(
absl::StrCat("k8s.deployment.name=foo,k8s.cluster.name=my-cluster,k8s."
"namespace.name=default,",
"service.name=foo-service,", "service.version=v1alpha3"));
EXPECT_EQ(obj.canonical_name_, "foo-service");
EXPECT_EQ(obj.canonical_revision_, "v1alpha3");
EXPECT_EQ(obj.workload_type_, WorkloadType::Deployment);
EXPECT_EQ(obj.workload_name_, "foo");
EXPECT_EQ(obj.namespace_name_, "default");
EXPECT_EQ(obj.cluster_name_, "my-cluster");
checkFlatNodeConversion(obj);
}
{
const auto r =
convertBaggageToWorkloadMetadata("type=pod,name=foo-pod-435,cluster=my-cluster,namespace="
"test,service=foo-service,revision=v1beta2");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("service")), "foo-service");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("revision")), "v1beta2");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("type")), PodSuffix);
EXPECT_EQ(absl::get<absl::string_view>(r->getField("workload")), "");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("name")), "foo-pod-435");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("namespace")), "test");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("cluster")), "my-cluster");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("app")), "");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("version")), "");
checkStructConversion(*r);
auto obj = WorkloadMetadataObject::fromBaggage(
absl::StrCat("k8s.pod.name=foo-pod-435,k8s.cluster.name=my-cluster,k8s."
"namespace.name=test,"
"service.name=foo-service,service.version=v1beta2"));
EXPECT_EQ(obj.canonical_name_, "foo-service");
EXPECT_EQ(obj.canonical_revision_, "v1beta2");
EXPECT_EQ(obj.workload_type_, WorkloadType::Pod);
EXPECT_EQ(obj.workload_name_, "foo-pod-435");
EXPECT_EQ(obj.instance_name_, "foo-pod-435");
EXPECT_EQ(obj.namespace_name_, "test");
EXPECT_EQ(obj.cluster_name_, "my-cluster");
checkFlatNodeConversion(obj);
}
{
const auto r =
convertBaggageToWorkloadMetadata("type=job,name=foo-job-435,cluster=my-cluster,namespace="
"test,service=foo-service,revision=v1beta4");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("service")), "foo-service");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("revision")), "v1beta4");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("type")), JobSuffix);
EXPECT_EQ(absl::get<absl::string_view>(r->getField("workload")), "");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("name")), "foo-job-435");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("namespace")), "test");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("cluster")), "my-cluster");
checkStructConversion(*r);
auto obj = WorkloadMetadataObject::fromBaggage(
absl::StrCat("k8s.job.name=foo-job-435,k8s.cluster.name=my-cluster,k8s."
"namespace.name=test,",
"service.name=foo-service,", "service.version=v1beta4"));
EXPECT_EQ(obj.canonical_name_, "foo-service");
EXPECT_EQ(obj.canonical_revision_, "v1beta4");
EXPECT_EQ(obj.workload_type_, WorkloadType::Job);
EXPECT_EQ(obj.workload_name_, "foo-job-435");
EXPECT_EQ(obj.instance_name_, "foo-job-435");
EXPECT_EQ(obj.namespace_name_, "test");
EXPECT_EQ(obj.cluster_name_, "my-cluster");
checkFlatNodeConversion(obj);
}
{
const auto r =
convertBaggageToWorkloadMetadata("type=cronjob,workload=foo-cronjob,cluster=my-cluster,"
"namespace=test,service=foo-service,revision=v1beta4");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("service")), "foo-service");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("revision")), "v1beta4");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("type")), CronJobSuffix);
EXPECT_EQ(absl::get<absl::string_view>(r->getField("workload")), "foo-cronjob");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("name")), "");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("namespace")), "test");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("cluster")), "my-cluster");
checkStructConversion(*r);
auto obj = WorkloadMetadataObject::fromBaggage(
absl::StrCat("k8s.cronjob.name=foo-cronjob,k8s.cluster.name=my-cluster,"
"k8s.namespace.name=test,",
"service.name=foo-service,", "service.version=v1beta4"));
EXPECT_EQ(obj.canonical_name_, "foo-service");
EXPECT_EQ(obj.canonical_revision_, "v1beta4");
EXPECT_EQ(obj.workload_type_, WorkloadType::CronJob);
EXPECT_EQ(obj.workload_name_, "foo-cronjob");
EXPECT_EQ(obj.namespace_name_, "test");
EXPECT_EQ(obj.cluster_name_, "my-cluster");
EXPECT_EQ(obj.app_name_, "");
EXPECT_EQ(obj.app_version_, "");
checkFlatNodeConversion(obj);
}
{
const auto r = convertBaggageToWorkloadMetadata(
"type=deployment,workload=foo,namespace=default,service=foo-service,revision=v1alpha3");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("service")), "foo-service");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("revision")), "v1alpha3");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("type")), DeploymentSuffix);
EXPECT_EQ(absl::get<absl::string_view>(r->getField("workload")), "foo");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("namespace")), "default");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("cluster")), "");
checkStructConversion(*r);
}
auto obj = WorkloadMetadataObject::fromBaggage(absl::StrCat(
"k8s.deployment.name=foo,k8s.namespace.name=default,", "service.name=foo-service,",
"service.version=v1alpha3,app.name=foo-app,app.version=v1"));
{
const auto r = convertBaggageToWorkloadMetadata("namespace=default");
EXPECT_EQ(absl::get<absl::string_view>(r->getField("namespace")), "default");
checkStructConversion(*r);
EXPECT_EQ(obj.canonical_name_, "foo-service");
EXPECT_EQ(obj.canonical_revision_, "v1alpha3");
EXPECT_EQ(obj.workload_type_, WorkloadType::Deployment);
EXPECT_EQ(obj.workload_name_, "foo");
EXPECT_EQ(obj.namespace_name_, "default");
EXPECT_EQ(obj.cluster_name_, "");
EXPECT_EQ(obj.app_name_, "foo-app");
EXPECT_EQ(obj.app_version_, "v1");
checkFlatNodeConversion(obj);
}
}
TEST(WorkloadMetadataObjectTest, ConvertFromEmpty) {
google::protobuf::Struct node;
auto obj = convertStructToWorkloadMetadata(node);
EXPECT_EQ(obj->serializeAsString(), "");
checkStructConversion(*obj);
TEST(WorkloadMetadataObjectTest, ConvertFromFlatNode) {
flatbuffers::FlatBufferBuilder fbb;
Wasm::Common::FlatNodeBuilder builder(fbb);
auto data = builder.Finish();
fbb.Finish(data);
auto buffer = fbb.Release();
const auto& node = *flatbuffers::GetRoot<Wasm::Common::FlatNode>(buffer.data());
auto obj = convertFlatNodeToWorkloadMetadata(node);
EXPECT_EQ(obj.baggage(), "k8s.pod.name=");
}
TEST(WorkloadMetadataObjectTest, ConvertFromEndpointMetadata) {
@ -173,8 +171,8 @@ TEST(WorkloadMetadataObjectTest, ConvertFromEndpointMetadata) {
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");
EXPECT_EQ(obj->baggage(), "k8s.pod.name=foo-pod,k8s.cluster.name=my-cluster,k8s.namespace."
"name=default,service.name=foo-service,service.version=v1");
}
} // namespace Common

View File

@ -0,0 +1,50 @@
/* Copyright 2020 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.
*/
namespace Wasm.Common;
table KeyVal {
key:string (key);
value:string;
}
// NodeInfo represents the information extracted from proxy node metadata.
table FlatNode {
// Name of the node. e.g. in k8s, name is the pod name.
name:string;
// Namespace that the node runs in.
namespace:string;
// K8s or vm workload attributes.
labels:[KeyVal];
owner:string;
workload_name:string;
// Platform metadata uses prefixed keys
// GCP uses gcp_* keys
platform_metadata:[KeyVal];
// Version identifier for the proxy.
istio_version:string;
// Unique identifier for the mesh. Taken from global mesh id parameter (or
// the configured trust domain when not specified).
mesh_id:string;
// List of short names for application containers that are using this proxy.
// This is only used for kubernetes, and is populated by the sidecar injector.
app_containers:[string];
// Identifier for the cluster to which this workload belongs (for k8s workloads).
cluster_id:string;
// instance ip addresses
instance_ips:[string];
}
root_type FlatNode;

View File

@ -0,0 +1,191 @@
/* Copyright 2020 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/proto_util.h"
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "extensions/common/util.h"
// WASM_PROLOG
#ifndef NULL_PLUGIN
#include "proxy_wasm_intrinsics.h"
#else // NULL_PLUGIN
#include "include/proxy-wasm/null_plugin.h"
#endif // NULL_PLUGIN
// END WASM_PROLOG
namespace Wasm {
namespace Common {
flatbuffers::DetachedBuffer
extractNodeFlatBufferFromStruct(const google::protobuf::Struct& metadata) {
flatbuffers::FlatBufferBuilder fbb;
flatbuffers::Offset<flatbuffers::String> name, namespace_, owner, workload_name, istio_version,
mesh_id, cluster_id;
std::vector<flatbuffers::Offset<KeyVal>> labels, platform_metadata;
std::vector<flatbuffers::Offset<flatbuffers::String>> app_containers;
std::vector<flatbuffers::Offset<flatbuffers::String>> ip_addrs;
for (const auto& it : metadata.fields()) {
if (it.first == "NAME") {
name = fbb.CreateString(it.second.string_value());
} else if (it.first == "NAMESPACE") {
namespace_ = fbb.CreateString(it.second.string_value());
} else if (it.first == "OWNER") {
owner = fbb.CreateString(it.second.string_value());
} else if (it.first == "WORKLOAD_NAME") {
workload_name = fbb.CreateString(it.second.string_value());
} else if (it.first == "ISTIO_VERSION") {
istio_version = fbb.CreateString(it.second.string_value());
} else if (it.first == "MESH_ID") {
mesh_id = fbb.CreateString(it.second.string_value());
} else if (it.first == "CLUSTER_ID") {
cluster_id = fbb.CreateString(it.second.string_value());
} else if (it.first == "LABELS") {
for (const auto& labels_it : it.second.struct_value().fields()) {
labels.push_back(CreateKeyVal(fbb, fbb.CreateString(labels_it.first),
fbb.CreateString(labels_it.second.string_value())));
}
} else if (it.first == "PLATFORM_METADATA") {
for (const auto& platform_it : it.second.struct_value().fields()) {
platform_metadata.push_back(
CreateKeyVal(fbb, fbb.CreateString(platform_it.first),
fbb.CreateString(platform_it.second.string_value())));
}
} else if (it.first == "APP_CONTAINERS") {
std::vector<absl::string_view> containers = absl::StrSplit(it.second.string_value(), ',');
for (const auto& container : containers) {
app_containers.push_back(fbb.CreateString(toStdStringView(container)));
}
} else if (it.first == "INSTANCE_IPS") {
std::vector<absl::string_view> ip_addresses = absl::StrSplit(it.second.string_value(), ',');
for (const auto& ip : ip_addresses) {
ip_addrs.push_back(fbb.CreateString(toStdStringView(ip)));
}
}
}
// finish pre-order construction
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<KeyVal>>> labels_offset,
platform_metadata_offset;
if (labels.size() > 0) {
labels_offset = fbb.CreateVectorOfSortedTables(&labels);
}
if (platform_metadata.size() > 0) {
platform_metadata_offset = fbb.CreateVectorOfSortedTables(&platform_metadata);
}
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>>
app_containers_offset;
if (app_containers.size() > 0) {
app_containers_offset = fbb.CreateVector(app_containers);
}
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>>
ip_addrs_offset;
if (ip_addrs.size() > 0) {
ip_addrs_offset = fbb.CreateVector(ip_addrs);
}
FlatNodeBuilder node(fbb);
node.add_name(name);
node.add_namespace_(namespace_);
node.add_owner(owner);
node.add_workload_name(workload_name);
node.add_istio_version(istio_version);
node.add_mesh_id(mesh_id);
node.add_cluster_id(cluster_id);
node.add_labels(labels_offset);
node.add_platform_metadata(platform_metadata_offset);
if (app_containers.size() > 0) {
node.add_app_containers(app_containers_offset);
}
if (ip_addrs.size() > 0) {
node.add_instance_ips(ip_addrs_offset);
}
auto data = node.Finish();
fbb.Finish(data);
return fbb.Release();
}
void extractStructFromNodeFlatBuffer(const FlatNode& node, google::protobuf::Struct* metadata) {
if (node.name()) {
(*metadata->mutable_fields())["NAME"].set_string_value(node.name()->str());
}
if (node.namespace_()) {
(*metadata->mutable_fields())["NAMESPACE"].set_string_value(node.namespace_()->str());
}
if (node.owner()) {
(*metadata->mutable_fields())["OWNER"].set_string_value(node.owner()->str());
}
if (node.workload_name()) {
(*metadata->mutable_fields())["WORKLOAD_NAME"].set_string_value(node.workload_name()->str());
}
if (node.istio_version()) {
(*metadata->mutable_fields())["ISTIO_VERSION"].set_string_value(node.istio_version()->str());
}
if (node.mesh_id()) {
(*metadata->mutable_fields())["MESH_ID"].set_string_value(node.mesh_id()->str());
}
if (node.cluster_id()) {
(*metadata->mutable_fields())["CLUSTER_ID"].set_string_value(node.cluster_id()->str());
}
if (node.labels()) {
auto* map = (*metadata->mutable_fields())["LABELS"].mutable_struct_value();
for (const auto keyval : *node.labels()) {
(*map->mutable_fields())[flatbuffers::GetString(keyval->key())].set_string_value(
flatbuffers::GetString(keyval->value()));
}
}
if (node.platform_metadata()) {
auto* map = (*metadata->mutable_fields())["PLATFORM_METADATA"].mutable_struct_value();
for (const auto keyval : *node.platform_metadata()) {
(*map->mutable_fields())[flatbuffers::GetString(keyval->key())].set_string_value(
flatbuffers::GetString(keyval->value()));
}
}
if (node.app_containers()) {
std::vector<std::string> containers;
for (const auto container : *node.app_containers()) {
containers.push_back(flatbuffers::GetString(container));
}
(*metadata->mutable_fields())["APP_CONTAINERS"].set_string_value(
absl::StrJoin(containers, ","));
}
if (node.instance_ips()) {
std::vector<std::string> ip_addrs;
for (const auto ip : *node.instance_ips()) {
ip_addrs.push_back(flatbuffers::GetString(ip));
}
(*metadata->mutable_fields())["INSTANCE_IPS"].set_string_value(absl::StrJoin(ip_addrs, ","));
}
}
bool serializeToStringDeterministic(const google::protobuf::Message& metadata,
std::string* metadata_bytes) {
google::protobuf::io::StringOutputStream md(metadata_bytes);
google::protobuf::io::CodedOutputStream mcs(&md);
mcs.SetSerializationDeterministic(true);
if (!metadata.SerializeToCodedStream(&mcs)) {
return false;
}
return true;
}
} // namespace Common
} // namespace Wasm

View File

@ -0,0 +1,40 @@
/* Copyright 2020 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 "extensions/common/node_info_generated.h"
#include "flatbuffers/flatbuffers.h"
#include "google/protobuf/struct.pb.h"
/**
* Utilities that require protobuf import.
*/
namespace Wasm {
namespace Common {
// Extract node info into a flatbuffer from a struct.
flatbuffers::DetachedBuffer
extractNodeFlatBufferFromStruct(const google::protobuf::Struct& metadata);
// Extract struct from a flatbuffer. This is an inverse of the above function.
void extractStructFromNodeFlatBuffer(const FlatNode& node, google::protobuf::Struct* metadata);
// Serialize deterministically a protobuf to a string.
bool serializeToStringDeterministic(const google::protobuf::Message& metadata,
std::string* metadata_bytes);
} // namespace Common
} // namespace Wasm

View File

@ -0,0 +1,220 @@
/* 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 "benchmark/benchmark.h"
#include "extensions/common/metadata_object.h"
#include "extensions/common/node_info_generated.h"
#include "extensions/common/proto_util.h"
#include "extensions/common/util.h"
#include "google/protobuf/util/json_util.h"
#include "source/common/common/base64.h"
#include "source/common/stream_info/filter_state_impl.h"
#include "source/extensions/filters/common/expr/cel_state.h"
#include "test/test_common/status_utility.h"
// WASM_PROLOG
#ifdef NULL_PLUGIN
namespace Wasm {
#endif // NULL_PLUGIN
// END WASM_PROLOG
namespace Common {
using namespace google::protobuf::util;
constexpr std::string_view node_metadata_json = R"###(
{
"NAME":"test_pod",
"NAMESPACE":"test_namespace",
"LABELS": {
"app": "productpage",
"version": "v1",
"pod-template-hash": "84975bc778"
},
"OWNER":"test_owner",
"WORKLOAD_NAME":"test_workload",
"PLATFORM_METADATA":{
"gcp_project":"test_project",
"gcp_cluster_location":"test_location",
"gcp_cluster_name":"test_cluster"
},
"ISTIO_VERSION":"istio-1.4",
"MESH_ID":"test-mesh"
}
)###";
constexpr std::string_view metadata_id_key = "envoy.wasm.metadata_exchange.downstream_id";
constexpr std::string_view metadata_key = "envoy.wasm.metadata_exchange.downstream";
constexpr std::string_view node_id = "test_pod.test_namespace";
static void setData(Envoy::StreamInfo::FilterStateImpl& filter_state, std::string_view key,
std::string_view value) {
Envoy::Extensions::Filters::Common::Expr::CelStatePrototype prototype;
auto state_ptr = std::make_unique<Envoy::Extensions::Filters::Common::Expr::CelState>(prototype);
state_ptr->setValue(toAbslStringView(value));
filter_state.setData(toAbslStringView(key), std::move(state_ptr),
Envoy::StreamInfo::FilterState::StateType::Mutable);
}
static const std::string& getData(Envoy::StreamInfo::FilterStateImpl& filter_state,
std::string_view key) {
return filter_state
.getDataReadOnly<Envoy::Extensions::Filters::Common::Expr::CelState>(toAbslStringView(key))
->value();
}
static void BM_ReadFlatBuffer(benchmark::State& state) {
google::protobuf::Struct metadata_struct;
JsonParseOptions json_parse_options;
ASSERT_OK(
JsonStringToMessage(std::string(node_metadata_json), &metadata_struct, json_parse_options));
auto out = extractNodeFlatBufferFromStruct(metadata_struct);
Envoy::StreamInfo::FilterStateImpl filter_state{
Envoy::StreamInfo::FilterState::LifeSpan::TopSpan};
setData(filter_state, metadata_key,
std::string_view(reinterpret_cast<const char*>(out.data()), out.size()));
size_t size = 0;
for (auto _ : state) {
auto buf = getData(filter_state, metadata_key);
auto peer = flatbuffers::GetRoot<FlatNode>(buf.data());
size += peer->workload_name()->size() + peer->namespace_()->size() +
peer->labels()->LookupByKey("app")->value()->size() +
peer->labels()->LookupByKey("version")->value()->size();
benchmark::DoNotOptimize(size);
}
}
BENCHMARK(BM_ReadFlatBuffer);
static void BM_WriteRawBytes(benchmark::State& state) {
google::protobuf::Struct metadata_struct;
JsonParseOptions json_parse_options;
ASSERT_OK(
JsonStringToMessage(std::string(node_metadata_json), &metadata_struct, json_parse_options));
auto bytes = metadata_struct.SerializeAsString();
Envoy::StreamInfo::FilterStateImpl filter_state{
Envoy::StreamInfo::FilterState::LifeSpan::TopSpan};
for (auto _ : state) {
setData(filter_state, metadata_id_key, node_id);
setData(filter_state, metadata_key, bytes);
}
}
BENCHMARK(BM_WriteRawBytes);
static void BM_WriteFlatBufferWithCache(benchmark::State& state) {
google::protobuf::Struct metadata_struct;
JsonParseOptions json_parse_options;
ASSERT_OK(
JsonStringToMessage(std::string(node_metadata_json), &metadata_struct, json_parse_options));
auto bytes = metadata_struct.SerializeAsString();
Envoy::StreamInfo::FilterStateImpl filter_state{
Envoy::StreamInfo::FilterState::LifeSpan::TopSpan};
std::unordered_map<std::string, std::string> cache;
for (auto _ : state) {
// lookup cache by key
auto nodeinfo_it = cache.find(std::string(node_id));
std::string node_info;
if (nodeinfo_it == cache.end()) {
google::protobuf::Struct test_struct;
test_struct.ParseFromArray(bytes.data(), bytes.size());
benchmark::DoNotOptimize(test_struct);
auto out = extractNodeFlatBufferFromStruct(test_struct);
node_info =
cache.emplace(node_id, std::string(reinterpret_cast<const char*>(out.data()), out.size()))
.first->second;
} else {
node_info = nodeinfo_it->second;
}
setData(filter_state, metadata_id_key, node_id);
setData(filter_state, metadata_key, node_info);
}
}
BENCHMARK(BM_WriteFlatBufferWithCache);
constexpr std::string_view node_flatbuffer_json = R"###(
{
"NAME":"test_pod",
"NAMESPACE":"default",
"CLUSTER_ID": "client-cluster",
"LABELS": {
"app": "productpage",
"version": "v1",
"service.istio.io/canonical-name": "productpage-v1",
"service.istio.io/canonical-revision": "version-1"
},
"OWNER": "kubernetes://apis/apps/v1/namespaces/default/deployments/productpage-v1",
"WORKLOAD_NAME":"productpage-v1"
}
)###";
// Measure decoding performance of x-envoy-peer-metadata.
static void BM_DecodeFlatBuffer(benchmark::State& state) {
// Construct a header from sample value.
google::protobuf::Struct metadata_struct;
JsonParseOptions json_parse_options;
ASSERT_OK(
JsonStringToMessage(std::string(node_flatbuffer_json), &metadata_struct, json_parse_options));
std::string metadata_bytes;
::Wasm::Common::serializeToStringDeterministic(metadata_struct, &metadata_bytes);
const std::string header_value =
Envoy::Base64::encode(metadata_bytes.data(), metadata_bytes.size());
size_t size = 0;
for (auto _ : state) {
auto bytes = Envoy::Base64::decodeWithoutPadding(header_value);
google::protobuf::Struct metadata;
metadata.ParseFromString(bytes);
auto fb = ::Wasm::Common::extractNodeFlatBufferFromStruct(metadata);
size += fb.size();
benchmark::DoNotOptimize(size);
}
}
BENCHMARK(BM_DecodeFlatBuffer);
// Measure decoding performance of baggage.
static void BM_DecodeBaggage(benchmark::State& state) {
// Construct a header from sample value.
google::protobuf::Struct metadata_struct;
JsonParseOptions json_parse_options;
ASSERT_OK(
JsonStringToMessage(std::string(node_flatbuffer_json), &metadata_struct, json_parse_options));
auto fb = ::Wasm::Common::extractNodeFlatBufferFromStruct(metadata_struct);
const auto& node = *flatbuffers::GetRoot<Wasm::Common::FlatNode>(fb.data());
const std::string baggage = Istio::Common::convertFlatNodeToWorkloadMetadata(node).baggage();
size_t size = 0;
for (auto _ : state) {
auto obj = Istio::Common::WorkloadMetadataObject::fromBaggage(baggage);
size += obj.namespace_name_.size();
benchmark::DoNotOptimize(size);
}
}
BENCHMARK(BM_DecodeBaggage);
} // namespace Common
// WASM_EPILOG
#ifdef NULL_PLUGIN
} // namespace Wasm
#endif

View File

@ -0,0 +1,157 @@
/* 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 "extensions/common/proto_util.h"
#include "extensions/common/node_info_generated.h"
#include "google/protobuf/struct.pb.h"
#include "google/protobuf/text_format.h"
#include "google/protobuf/util/json_util.h"
#include "gtest/gtest.h"
// WASM_PROLOG
#ifdef NULL_PLUGIN
namespace Wasm {
#endif // NULL_PLUGIN
// END WASM_PROLOG
namespace Common {
using namespace google::protobuf;
using namespace google::protobuf::util;
constexpr std::string_view node_metadata_json = R"###(
{
"NAME":"test_pod",
"NAMESPACE":"test_namespace",
"OWNER":"test_owner",
"WORKLOAD_NAME":"test_workload",
"ISTIO_VERSION":"1.8",
"MESH_ID":"istio-mesh",
"CLUSTER_ID":"test-cluster",
"LABELS":{
"app":"test",
"version":"v1"
},
"PLATFORM_METADATA":{
"gcp_cluster_location":"test_location",
"gcp_cluster_name":"test_cluster",
"gcp_project":"test_project"
},
"APP_CONTAINERS": "hello,test",
"INSTANCE_IPS": "10.10.10.1,10.10.10.2,10.10.10.3"
}
)###";
constexpr std::string_view node_metadata_json_with_missing_lists = R"###(
{
"NAME":"test_pod",
"NAMESPACE":"test_namespace",
"OWNER":"test_owner",
"WORKLOAD_NAME":"test_workload",
"ISTIO_VERSION":"1.8",
"MESH_ID":"istio-mesh",
"CLUSTER_ID":"test-cluster",
"LABELS":{
"app":"test",
"version":"v1"
},
"PLATFORM_METADATA":{
"gcp_cluster_location":"test_location",
"gcp_cluster_name":"test_cluster",
"gcp_project":"test_project"
},
}
)###";
// Test all possible metadata field.
TEST(ProtoUtilTest, extractNodeMetadata) {
google::protobuf::Struct metadata_struct;
JsonParseOptions json_parse_options;
EXPECT_TRUE(
JsonStringToMessage(std::string(node_metadata_json), &metadata_struct, json_parse_options)
.ok());
auto out = extractNodeFlatBufferFromStruct(metadata_struct);
auto peer = flatbuffers::GetRoot<FlatNode>(out.data());
EXPECT_EQ(peer->name()->string_view(), "test_pod");
EXPECT_EQ(peer->namespace_()->string_view(), "test_namespace");
EXPECT_EQ(peer->owner()->string_view(), "test_owner");
EXPECT_EQ(peer->workload_name()->string_view(), "test_workload");
EXPECT_EQ(peer->platform_metadata()->Get(2)->key()->string_view(), "gcp_project");
EXPECT_EQ(peer->platform_metadata()->Get(2)->value()->string_view(), "test_project");
EXPECT_EQ(peer->app_containers()->size(), 2);
EXPECT_EQ(peer->instance_ips()->size(), 3);
EXPECT_EQ(peer->cluster_id()->string_view(), "test-cluster");
}
// Test all possible metadata field.
TEST(ProtoUtilTest, extractNodeMetadataWithMissingLists) {
google::protobuf::Struct metadata_struct;
JsonParseOptions json_parse_options;
EXPECT_TRUE(JsonStringToMessage(std::string(node_metadata_json_with_missing_lists),
&metadata_struct, json_parse_options)
.ok());
auto out = extractNodeFlatBufferFromStruct(metadata_struct);
auto peer = flatbuffers::GetRoot<FlatNode>(out.data());
EXPECT_EQ(peer->name()->string_view(), "test_pod");
EXPECT_EQ(peer->namespace_()->string_view(), "test_namespace");
EXPECT_EQ(peer->owner()->string_view(), "test_owner");
EXPECT_EQ(peer->workload_name()->string_view(), "test_workload");
EXPECT_EQ(peer->platform_metadata()->Get(2)->key()->string_view(), "gcp_project");
EXPECT_EQ(peer->platform_metadata()->Get(2)->value()->string_view(), "test_project");
EXPECT_EQ(peer->app_containers(), nullptr);
EXPECT_EQ(peer->instance_ips(), nullptr);
EXPECT_EQ(peer->cluster_id()->string_view(), "test-cluster");
}
// Test roundtripping
TEST(ProtoUtilTest, Rountrip) {
google::protobuf::Struct metadata_struct;
JsonParseOptions json_parse_options;
EXPECT_TRUE(
JsonStringToMessage(std::string(node_metadata_json), &metadata_struct, json_parse_options)
.ok());
auto out = extractNodeFlatBufferFromStruct(metadata_struct);
auto peer = flatbuffers::GetRoot<FlatNode>(out.data());
google::protobuf::Struct output_struct;
extractStructFromNodeFlatBuffer(*peer, &output_struct);
// Validate serialized bytes
std::string input_bytes;
EXPECT_TRUE(serializeToStringDeterministic(metadata_struct, &input_bytes));
std::string output_bytes;
EXPECT_TRUE(serializeToStringDeterministic(output_struct, &output_bytes));
EXPECT_EQ(input_bytes, output_bytes)
<< metadata_struct.DebugString() << output_struct.DebugString();
}
// Test roundtrip for an empty struct (for corner cases)
TEST(ProtoUtilTest, RountripEmpty) {
google::protobuf::Struct metadata_struct;
auto out = extractNodeFlatBufferFromStruct(metadata_struct);
auto peer = flatbuffers::GetRoot<FlatNode>(out.data());
google::protobuf::Struct output_struct;
extractStructFromNodeFlatBuffer(*peer, &output_struct);
EXPECT_EQ(0, output_struct.fields().size());
}
} // namespace Common
// WASM_EPILOG
#ifdef NULL_PLUGIN
} // namespace Wasm
#endif

219
extensions/common/util.cc Normal file
View File

@ -0,0 +1,219 @@
/* 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 "extensions/common/util.h"
#include "absl/strings/str_cat.h"
namespace Wasm {
namespace Common {
namespace {
// This replicates the flag lists in envoyproxy/envoy, because the property
// access API does not support returning response flags as a short string since
// it is not owned by any object and always generated on demand:
// https://github.com/envoyproxy/envoy/blob/v1.18.3/source/common/stream_info/utility.h#L21
constexpr static absl::string_view DOWNSTREAM_CONNECTION_TERMINATION = "DC";
constexpr static absl::string_view FAILED_LOCAL_HEALTH_CHECK = "LH";
constexpr static absl::string_view NO_HEALTHY_UPSTREAM = "UH";
constexpr static absl::string_view UPSTREAM_REQUEST_TIMEOUT = "UT";
constexpr static absl::string_view LOCAL_RESET = "LR";
constexpr static absl::string_view UPSTREAM_REMOTE_RESET = "UR";
constexpr static absl::string_view UPSTREAM_CONNECTION_FAILURE = "UF";
constexpr static absl::string_view UPSTREAM_CONNECTION_TERMINATION = "UC";
constexpr static absl::string_view UPSTREAM_OVERFLOW = "UO";
constexpr static absl::string_view UPSTREAM_RETRY_LIMIT_EXCEEDED = "URX";
constexpr static absl::string_view NO_ROUTE_FOUND = "NR";
constexpr static absl::string_view DELAY_INJECTED = "DI";
constexpr static absl::string_view FAULT_INJECTED = "FI";
constexpr static absl::string_view RATE_LIMITED = "RL";
constexpr static absl::string_view UNAUTHORIZED_EXTERNAL_SERVICE = "UAEX";
constexpr static absl::string_view RATELIMIT_SERVICE_ERROR = "RLSE";
constexpr static absl::string_view STREAM_IDLE_TIMEOUT = "SI";
constexpr static absl::string_view INVALID_ENVOY_REQUEST_HEADERS = "IH";
constexpr static absl::string_view DOWNSTREAM_PROTOCOL_ERROR = "DPE";
constexpr static absl::string_view UPSTREAM_MAX_STREAM_DURATION_REACHED = "UMSDR";
constexpr static absl::string_view RESPONSE_FROM_CACHE_FILTER = "RFCF";
constexpr static absl::string_view NO_FILTER_CONFIG_FOUND = "NFCF";
constexpr static absl::string_view DURATION_TIMEOUT = "DT";
constexpr static absl::string_view UPSTREAM_PROTOCOL_ERROR = "UPE";
constexpr static absl::string_view NO_CLUSTER_FOUND = "NC";
constexpr static absl::string_view OVERLOAD_MANAGER = "OM";
constexpr static absl::string_view DNS_RESOLUTION_FAILURE = "DF";
enum ResponseFlag {
FailedLocalHealthCheck = 0x1,
NoHealthyUpstream = 0x2,
UpstreamRequestTimeout = 0x4,
LocalReset = 0x8,
UpstreamRemoteReset = 0x10,
UpstreamConnectionFailure = 0x20,
UpstreamConnectionTermination = 0x40,
UpstreamOverflow = 0x80,
NoRouteFound = 0x100,
DelayInjected = 0x200,
FaultInjected = 0x400,
RateLimited = 0x800,
UnauthorizedExternalService = 0x1000,
RateLimitServiceError = 0x2000,
DownstreamConnectionTermination = 0x4000,
UpstreamRetryLimitExceeded = 0x8000,
StreamIdleTimeout = 0x10000,
InvalidEnvoyRequestHeaders = 0x20000,
DownstreamProtocolError = 0x40000,
UpstreamMaxStreamDurationReached = 0x80000,
ResponseFromCacheFilter = 0x100000,
NoFilterConfigFound = 0x200000,
DurationTimeout = 0x400000,
UpstreamProtocolError = 0x800000,
NoClusterFound = 0x1000000,
OverloadManager = 0x2000000,
DnsResolutionFailed = 0x4000000,
LastFlag = DnsResolutionFailed,
};
void appendString(std::string& result, const absl::string_view& append) {
if (result.empty()) {
result = std::string(append);
} else {
absl::StrAppend(&result, ",", append);
}
}
} // namespace
const std::string parseResponseFlag(uint64_t response_flag) {
std::string result;
if (response_flag & FailedLocalHealthCheck) {
appendString(result, FAILED_LOCAL_HEALTH_CHECK);
}
if (response_flag & NoHealthyUpstream) {
appendString(result, NO_HEALTHY_UPSTREAM);
}
if (response_flag & UpstreamRequestTimeout) {
appendString(result, UPSTREAM_REQUEST_TIMEOUT);
}
if (response_flag & LocalReset) {
appendString(result, LOCAL_RESET);
}
if (response_flag & UpstreamRemoteReset) {
appendString(result, UPSTREAM_REMOTE_RESET);
}
if (response_flag & UpstreamConnectionFailure) {
appendString(result, UPSTREAM_CONNECTION_FAILURE);
}
if (response_flag & UpstreamConnectionTermination) {
appendString(result, UPSTREAM_CONNECTION_TERMINATION);
}
if (response_flag & UpstreamOverflow) {
appendString(result, UPSTREAM_OVERFLOW);
}
if (response_flag & NoRouteFound) {
appendString(result, NO_ROUTE_FOUND);
}
if (response_flag & DelayInjected) {
appendString(result, DELAY_INJECTED);
}
if (response_flag & FaultInjected) {
appendString(result, FAULT_INJECTED);
}
if (response_flag & RateLimited) {
appendString(result, RATE_LIMITED);
}
if (response_flag & UnauthorizedExternalService) {
appendString(result, UNAUTHORIZED_EXTERNAL_SERVICE);
}
if (response_flag & RateLimitServiceError) {
appendString(result, RATELIMIT_SERVICE_ERROR);
}
if (response_flag & DownstreamConnectionTermination) {
appendString(result, DOWNSTREAM_CONNECTION_TERMINATION);
}
if (response_flag & UpstreamRetryLimitExceeded) {
appendString(result, UPSTREAM_RETRY_LIMIT_EXCEEDED);
}
if (response_flag & StreamIdleTimeout) {
appendString(result, STREAM_IDLE_TIMEOUT);
}
if (response_flag & InvalidEnvoyRequestHeaders) {
appendString(result, INVALID_ENVOY_REQUEST_HEADERS);
}
if (response_flag & DownstreamProtocolError) {
appendString(result, DOWNSTREAM_PROTOCOL_ERROR);
}
if (response_flag & UpstreamMaxStreamDurationReached) {
appendString(result, UPSTREAM_MAX_STREAM_DURATION_REACHED);
}
if (response_flag & ResponseFromCacheFilter) {
appendString(result, RESPONSE_FROM_CACHE_FILTER);
}
if (response_flag & NoFilterConfigFound) {
appendString(result, NO_FILTER_CONFIG_FOUND);
}
if (response_flag & DurationTimeout) {
appendString(result, DURATION_TIMEOUT);
}
if (response_flag & UpstreamProtocolError) {
appendString(result, UPSTREAM_PROTOCOL_ERROR);
}
if (response_flag & NoClusterFound) {
appendString(result, NO_CLUSTER_FOUND);
}
if (response_flag & OverloadManager) {
appendString(result, OVERLOAD_MANAGER);
}
if (response_flag & DnsResolutionFailed) {
appendString(result, DNS_RESOLUTION_FAILURE);
}
if (response_flag >= (LastFlag << 1)) {
// Response flag integer overflows. Append the integer to avoid information
// loss.
appendString(result, std::to_string(response_flag));
}
return result.empty() ? ::Wasm::Common::NONE : result;
}
} // namespace Common
} // namespace Wasm

44
extensions/common/util.h Normal file
View File

@ -0,0 +1,44 @@
/* 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 <string>
#include "absl/strings/string_view.h"
namespace Wasm {
namespace Common {
// None response flag.
const char NONE[] = "-";
// Parses an integer response flag into a readable short string.
const std::string parseResponseFlag(uint64_t response_flag);
// Used for converting sanctioned uses of std string_view (e.g. extensions) to
// absl::string_view for internal use.
inline absl::string_view toAbslStringView(std::string_view view) {
return absl::string_view(view.data(), view.size());
}
// Used for converting internal absl::string_view to sanctioned uses of std
// string_view (e.g. extensions).
inline std::string_view toStdStringView(absl::string_view view) {
return std::string_view(view.data(), view.size());
}
} // namespace Common
} // namespace Wasm

View File

@ -0,0 +1,53 @@
/* 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 "extensions/common/util.h"
#include "gtest/gtest.h"
namespace Wasm {
namespace Common {
namespace {
TEST(WasmCommonUtilsTest, ParseResponseFlag) {
std::vector<std::pair<uint64_t, std::string>> expected = {
std::make_pair(0x1, "LH"), std::make_pair(0x2, "UH"), std::make_pair(0x4, "UT"),
std::make_pair(0x8, "LR"), std::make_pair(0x10, "UR"), std::make_pair(0x20, "UF"),
std::make_pair(0x40, "UC"), std::make_pair(0x80, "UO"), std::make_pair(0x100, "NR"),
std::make_pair(0x200, "DI"), std::make_pair(0x400, "FI"), std::make_pair(0x800, "RL"),
std::make_pair(0x1000, "UAEX"), std::make_pair(0x2000, "RLSE"), std::make_pair(0x4000, "DC"),
std::make_pair(0x8000, "URX"), std::make_pair(0x10000, "SI"), std::make_pair(0x20000, "IH"),
std::make_pair(0x40000, "DPE"),
};
for (const auto& test_case : expected) {
EXPECT_EQ(test_case.second, parseResponseFlag(test_case.first));
}
// No flag is set.
{ EXPECT_EQ("-", parseResponseFlag(0x0)); }
// Test combinations.
// These are not real use cases, but are used to cover multiple response flags
// case.
{ EXPECT_EQ("UT,DI,FI", parseResponseFlag(0x604)); }
// Test overflow.
{ EXPECT_EQ("DPE,134479872", parseResponseFlag(0x8040000)); }
}
} // namespace
} // namespace Common
} // namespace Wasm

View File

@ -0,0 +1,30 @@
licenses(["notice"])
genrule(
name = "nlohmann_json_hpp",
srcs = ["@com_github_nlohmann_json_single_header//file"],
outs = ["nlohmann_json.hpp"],
cmd = "cp $< $@",
visibility = ["//visibility:public"],
)
cc_library(
name = "json_util",
srcs = ["json_util.cc"],
hdrs = [
"json_util.h",
":nlohmann_json_hpp",
],
copts = ["-UNULL_PLUGIN"],
visibility = ["//visibility:public"],
deps = [
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:optional",
],
)
exports_files([
"base64.h",
"json_util.cc",
"json_util.h",
])

View File

@ -0,0 +1,196 @@
/* 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.
*
* From
* https://github.com/envoyproxy/envoy/blob/master/source/common/common/base64.{h,cc}
*/
#pragma once
#include <string>
class Base64 {
public:
static std::string encode(const char* input, uint64_t length, bool add_padding);
static std::string encode(const char* input, uint64_t length) {
return encode(input, length, true);
}
static std::string decodeWithoutPadding(std::string_view input);
};
// clang-format off
inline constexpr char CHAR_TABLE[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
inline constexpr unsigned char REVERSE_LOOKUP_TABLE[256] = {
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 64, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64};
// clang-format on
inline bool decodeBase(const uint8_t cur_char, uint64_t pos, std::string& ret,
const unsigned char* const reverse_lookup_table) {
const unsigned char c = reverse_lookup_table[static_cast<uint32_t>(cur_char)];
if (c == 64) {
// Invalid character
return false;
}
switch (pos % 4) {
case 0:
ret.push_back(c << 2);
break;
case 1:
ret.back() |= c >> 4;
ret.push_back(c << 4);
break;
case 2:
ret.back() |= c >> 2;
ret.push_back(c << 6);
break;
case 3:
ret.back() |= c;
break;
}
return true;
}
inline bool decodeLast(const uint8_t cur_char, uint64_t pos, std::string& ret,
const unsigned char* const reverse_lookup_table) {
const unsigned char c = reverse_lookup_table[static_cast<uint32_t>(cur_char)];
if (c == 64) {
// Invalid character
return false;
}
switch (pos % 4) {
case 0:
return false;
case 1:
ret.back() |= c >> 4;
return (c & 0b1111) == 0;
case 2:
ret.back() |= c >> 2;
return (c & 0b11) == 0;
case 3:
ret.back() |= c;
break;
}
return true;
}
inline void encodeBase(const uint8_t cur_char, uint64_t pos, uint8_t& next_c, std::string& ret,
const char* const char_table) {
switch (pos % 3) {
case 0:
ret.push_back(char_table[cur_char >> 2]);
next_c = (cur_char & 0x03) << 4;
break;
case 1:
ret.push_back(char_table[next_c | (cur_char >> 4)]);
next_c = (cur_char & 0x0f) << 2;
break;
case 2:
ret.push_back(char_table[next_c | (cur_char >> 6)]);
ret.push_back(char_table[cur_char & 0x3f]);
next_c = 0;
break;
}
}
inline void encodeLast(uint64_t pos, uint8_t last_char, std::string& ret,
const char* const char_table, bool add_padding) {
switch (pos % 3) {
case 1:
ret.push_back(char_table[last_char]);
if (add_padding) {
ret.push_back('=');
ret.push_back('=');
}
break;
case 2:
ret.push_back(char_table[last_char]);
if (add_padding) {
ret.push_back('=');
}
break;
default:
break;
}
}
inline std::string Base64::encode(const char* input, uint64_t length, bool add_padding) {
uint64_t output_length = (length + 2) / 3 * 4;
std::string ret;
ret.reserve(output_length);
uint64_t pos = 0;
uint8_t next_c = 0;
for (uint64_t i = 0; i < length; ++i) {
encodeBase(input[i], pos++, next_c, ret, CHAR_TABLE);
}
encodeLast(pos, next_c, ret, CHAR_TABLE, add_padding);
return ret;
}
inline std::string Base64::decodeWithoutPadding(std::string_view input) {
if (input.empty()) {
return EMPTY_STRING;
}
// At most last two chars can be '='.
size_t n = input.length();
if (input[n - 1] == '=') {
n--;
if (n > 0 && input[n - 1] == '=') {
n--;
}
}
// Last position before "valid" padding character.
uint64_t last = n - 1;
// Determine output length.
size_t max_length = (n + 3) / 4 * 3;
if (n % 4 == 3) {
max_length -= 1;
}
if (n % 4 == 2) {
max_length -= 2;
}
std::string ret;
ret.reserve(max_length);
for (uint64_t i = 0; i < last; ++i) {
if (!decodeBase(input[i], i, ret, REVERSE_LOOKUP_TABLE)) {
return EMPTY_STRING;
}
}
if (!decodeLast(input[last], last, ret, REVERSE_LOOKUP_TABLE)) {
return EMPTY_STRING;
}
ASSERT(ret.size() == max_length);
return ret;
}

View File

@ -0,0 +1,184 @@
/* Copyright 2020 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/wasm/json_util.h"
#include "absl/strings/numbers.h"
namespace Wasm {
namespace Common {
std::optional<JsonObject> JsonParse(std::string_view str) {
const auto result = JsonObject::parse(str, nullptr, false);
if (result.is_discarded() || !result.is_object()) {
return std::nullopt;
}
return result;
}
template <>
std::pair<std::optional<int64_t>, JsonParserResultDetail>
JsonValueAs<int64_t>(const JsonObject& j) {
if (j.is_number()) {
return std::make_pair(j.get<int64_t>(), JsonParserResultDetail::OK);
} else if (j.is_string()) {
int64_t result = 0;
if (absl::SimpleAtoi(j.get_ref<std::string const&>(), &result)) {
return std::make_pair(result, JsonParserResultDetail::OK);
} else {
return std::make_pair(std::nullopt, JsonParserResultDetail::INVALID_VALUE);
}
}
return std::make_pair(std::nullopt, JsonParserResultDetail::TYPE_ERROR);
}
template <>
std::pair<std::optional<uint64_t>, JsonParserResultDetail>
JsonValueAs<uint64_t>(const JsonObject& j) {
if (j.is_number()) {
return std::make_pair(j.get<uint64_t>(), JsonParserResultDetail::OK);
} else if (j.is_string()) {
uint64_t result = 0;
if (absl::SimpleAtoi(j.get_ref<std::string const&>(), &result)) {
return std::make_pair(result, JsonParserResultDetail::OK);
} else {
return std::make_pair(std::nullopt, JsonParserResultDetail::INVALID_VALUE);
}
}
return std::make_pair(std::nullopt, JsonParserResultDetail::TYPE_ERROR);
}
template <>
std::pair<std::optional<std::string_view>, JsonParserResultDetail>
JsonValueAs<std::string_view>(const JsonObject& j) {
if (j.is_string()) {
return std::make_pair(std::string_view(j.get_ref<std::string const&>()),
JsonParserResultDetail::OK);
}
return std::make_pair(std::nullopt, JsonParserResultDetail::TYPE_ERROR);
}
template <>
std::pair<std::optional<std::string>, JsonParserResultDetail>
JsonValueAs<std::string>(const JsonObject& j) {
if (j.is_string()) {
return std::make_pair(j.get_ref<std::string const&>(), JsonParserResultDetail::OK);
}
return std::make_pair(std::nullopt, JsonParserResultDetail::TYPE_ERROR);
}
template <>
std::pair<std::optional<bool>, JsonParserResultDetail> JsonValueAs<bool>(const JsonObject& j) {
if (j.is_boolean()) {
return std::make_pair(j.get<bool>(), JsonParserResultDetail::OK);
}
if (j.is_string()) {
const std::string& v = j.get_ref<std::string const&>();
if (v == "true") {
return std::make_pair(true, JsonParserResultDetail::OK);
} else if (v == "false") {
return std::make_pair(false, JsonParserResultDetail::OK);
} else {
return std::make_pair(std::nullopt, JsonParserResultDetail::INVALID_VALUE);
}
}
return std::make_pair(std::nullopt, JsonParserResultDetail::TYPE_ERROR);
}
template <>
std::pair<std::optional<std::vector<std::string_view>>, JsonParserResultDetail>
JsonValueAs<std::vector<std::string_view>>(const JsonObject& j) {
std::pair<std::optional<std::vector<std::string_view>>, JsonParserResultDetail> values =
std::make_pair(std::nullopt, JsonParserResultDetail::OK);
if (j.is_array()) {
for (const auto& elt : j) {
if (!elt.is_string()) {
values.first = std::nullopt;
values.second = JsonParserResultDetail::TYPE_ERROR;
return values;
}
if (!values.first.has_value()) {
values.first = std::vector<std::string_view>();
}
values.first->emplace_back(elt.get_ref<std::string const&>());
}
return values;
}
values.second = JsonParserResultDetail::TYPE_ERROR;
return values;
}
template <>
std::pair<std::optional<JsonObject>, JsonParserResultDetail>
JsonValueAs<JsonObject>(const JsonObject& j) {
if (j.is_object()) {
return std::make_pair(j.get<JsonObject>(), JsonParserResultDetail::OK);
}
return std::make_pair(std::nullopt, JsonParserResultDetail::TYPE_ERROR);
}
bool JsonArrayIterate(const JsonObject& j, std::string_view field,
const std::function<bool(const JsonObject& elt)>& visitor) {
auto it = j.find(field);
if (it == j.end()) {
return true;
}
if (!it.value().is_array()) {
return false;
}
for (const auto& elt : it.value().items()) {
if (!visitor(elt.value())) {
return false;
}
}
return true;
}
bool JsonObjectIterate(const JsonObject& j, std::string_view field,
const std::function<bool(std::string key)>& visitor) {
auto it = j.find(field);
if (it == j.end()) {
return true;
}
if (!it.value().is_object()) {
return false;
}
for (const auto& elt : it.value().items()) {
auto json_value = JsonValueAs<std::string>(elt.key());
if (json_value.second != JsonParserResultDetail::OK) {
return false;
}
if (!visitor(json_value.first.value())) {
return false;
}
}
return true;
}
bool JsonObjectIterate(const JsonObject& j, const std::function<bool(std::string key)>& visitor) {
for (const auto& elt : j.items()) {
auto json_value = JsonValueAs<std::string>(elt.key());
if (json_value.second != JsonParserResultDetail::OK) {
return false;
}
if (!visitor(json_value.first.value())) {
return false;
}
}
return true;
}
} // namespace Common
} // namespace Wasm

View File

@ -0,0 +1,115 @@
/* Copyright 2020 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 <optional>
#include "extensions/common/wasm/nlohmann_json.hpp"
/**
* Utilities for working with JSON without exceptions.
*/
namespace Wasm {
namespace Common {
using JsonObject = ::nlohmann::json;
enum JsonParserResultDetail {
UNKNOWN,
OK,
OUT_OF_RANGE,
TYPE_ERROR,
INVALID_VALUE,
};
std::optional<JsonObject> JsonParse(std::string_view str);
template <typename T>
std::pair<std::optional<T>, JsonParserResultDetail> JsonValueAs(const JsonObject&) {
static_assert(true, "Unsupported Type");
}
template <>
std::pair<std::optional<std::string_view>, JsonParserResultDetail>
JsonValueAs<std::string_view>(const JsonObject& j);
template <>
std::pair<std::optional<std::string>, JsonParserResultDetail>
JsonValueAs<std::string>(const JsonObject& j);
template <>
std::pair<std::optional<int64_t>, JsonParserResultDetail> JsonValueAs<int64_t>(const JsonObject& j);
template <>
std::pair<std::optional<uint64_t>, JsonParserResultDetail>
JsonValueAs<uint64_t>(const JsonObject& j);
template <>
std::pair<std::optional<bool>, JsonParserResultDetail> JsonValueAs<bool>(const JsonObject& j);
template <>
std::pair<std::optional<JsonObject>, JsonParserResultDetail>
JsonValueAs<JsonObject>(const JsonObject& j);
template <>
std::pair<std::optional<std::vector<std::string_view>>, JsonParserResultDetail>
JsonValueAs<std::vector<std::string_view>>(const JsonObject& j);
template <class T> class JsonGetField {
public:
JsonGetField(const JsonObject& j, std::string_view field);
const JsonParserResultDetail& detail() { return detail_; }
T value() { return object_; }
T value_or(T v) {
if (detail_ != JsonParserResultDetail::OK)
return v;
else
return object_;
};
private:
JsonParserResultDetail detail_;
T object_;
};
template <class T> JsonGetField<T>::JsonGetField(const JsonObject& j, std::string_view field) {
auto it = j.find(field);
if (it == j.end()) {
detail_ = JsonParserResultDetail::OUT_OF_RANGE;
return;
}
auto value = JsonValueAs<T>(it.value());
detail_ = value.second;
if (value.first.has_value()) {
object_ = value.first.value();
}
}
// Iterate over an optional array field.
// Returns false if set and not an array, or any of the visitor calls returns
// false.
bool JsonArrayIterate(const JsonObject& j, std::string_view field,
const std::function<bool(const JsonObject& elt)>& visitor);
// Iterate over an optional object field key set.
// Returns false if set and not an object, or any of the visitor calls returns
// false.
bool JsonObjectIterate(const JsonObject& j, std::string_view field,
const std::function<bool(std::string key)>& visitor);
bool JsonObjectIterate(const JsonObject& j, const std::function<bool(std::string key)>& visitor);
} // namespace Common
} // namespace Wasm

View File

@ -0,0 +1,50 @@
licenses(["notice"]) # Apache 2
load(
"@envoy//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_package",
)
envoy_package()
envoy_cc_library(
name = "metadata_exchange_lib",
srcs = [
"config.cc",
"plugin.cc",
],
hdrs = [
"plugin.h",
],
repository = "@envoy",
visibility = ["//visibility:public"],
deps = [
"//extensions/common:context",
"//extensions/common:json_util",
"//extensions/common:proto_util",
"@envoy//source/common/common:base64_lib",
"@envoy//source/extensions/common/wasm/ext:declare_property_cc_proto",
"@proxy_wasm_cpp_host//:null_lib",
],
)
proto_library(
name = "config_proto",
srcs = ["config.proto"],
deps = [
"@com_google_protobuf//:wrappers_proto",
],
)
proto_library(
name = "declare_property_proto",
srcs = ["declare_property.proto"],
)
cc_proto_library(
name = "declare_property_proto_cc",
deps = [":declare_property_proto"],
)
exports_files(["base64.h"])

View File

@ -0,0 +1,34 @@
/* 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 "include/proxy-wasm/null_plugin.h"
#include "source/common/common/base64.h"
namespace proxy_wasm {
namespace null_plugin {
namespace MetadataExchange {
namespace Plugin {
NullPluginRegistry* context_registry_{};
} // namespace Plugin
// Registration glue
RegisterNullVmPluginFactory
register_http_metadata_exchange_filter("envoy.wasm.metadata_exchange", []() {
return std::make_unique<NullPlugin>(Plugin::context_registry_);
});
} // namespace MetadataExchange
} // namespace null_plugin
} // namespace proxy_wasm

View File

@ -0,0 +1,66 @@
---
title: Metadata Exchange Config
description: Configuration for Metadata Exchange Filter.
location: https://istio.io/docs/reference/config/proxy_extensions/metadata_exchange.html
layout: protoc-gen-docs
generator: protoc-gen-docs
weight: 20
number_of_entries: 2
---
<h2 id="PluginConfig">PluginConfig</h2>
<section>
<table class="message-fields">
<thead>
<tr>
<th>Field</th>
<th>Type</th>
<th>Description</th>
<th>Required</th>
</tr>
</thead>
<tbody>
<tr id="PluginConfig-max_peer_cache_size">
<td><code>max_peer_cache_size</code></td>
<td><code><a href="#google-protobuf-UInt32Value">UInt32Value</a></code></td>
<td>
<p>maximum size of the peer metadata cache.
A long lived proxy that connects with many transient peers can build up a
large cache. To turn off the cache, set this field to zero.</p>
</td>
<td>
No
</td>
</tr>
</tbody>
</table>
</section>
<h2 id="google-protobuf-UInt32Value">google.protobuf.UInt32Value</h2>
<section>
<p>Wrapper message for <code>uint32</code>.</p>
<p>The JSON representation for <code>UInt32Value</code> is JSON number.</p>
<table class="message-fields">
<thead>
<tr>
<th>Field</th>
<th>Type</th>
<th>Description</th>
<th>Required</th>
</tr>
</thead>
<tbody>
<tr id="google-protobuf-UInt32Value-value">
<td><code>value</code></td>
<td><code>uint32</code></td>
<td>
<p>The uint32 value.</p>
</td>
<td>
No
</td>
</tr>
</tbody>
</table>
</section>

View File

@ -0,0 +1,36 @@
/* 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";
// clang-format off
// $title: Metadata Exchange Config
// $description: Configuration for Metadata Exchange Filter.
// $location: https://istio.io/docs/reference/config/proxy_extensions/metadata_exchange.html
// $weight: 20
// clang-format on
package metadata_exchange;
import "google/protobuf/wrappers.proto";
// next id: 2
message PluginConfig {
// maximum size of the peer metadata cache.
// A long lived proxy that connects with many transient peers can build up a
// large cache. To turn off the cache, set this field to zero.
google.protobuf.UInt32Value max_peer_cache_size = 1;
}

View File

@ -0,0 +1,127 @@
---
title: envoy.source.extensions.common.wasm
layout: protoc-gen-docs
generator: protoc-gen-docs
number_of_entries: 3
---
<h2 id="DeclarePropertyArguments">DeclarePropertyArguments</h2>
<section>
<table class="message-fields">
<thead>
<tr>
<th>Field</th>
<th>Type</th>
<th>Description</th>
<th>Required</th>
</tr>
</thead>
<tbody>
<tr id="DeclarePropertyArguments-name">
<td><code>name</code></td>
<td><code>string</code></td>
<td>
</td>
<td>
No
</td>
</tr>
<tr id="DeclarePropertyArguments-readonly">
<td><code>readonly</code></td>
<td><code>bool</code></td>
<td>
</td>
<td>
No
</td>
</tr>
<tr id="DeclarePropertyArguments-type">
<td><code>type</code></td>
<td><code><a href="#WasmType">WasmType</a></code></td>
<td>
</td>
<td>
No
</td>
</tr>
<tr id="DeclarePropertyArguments-schema">
<td><code>schema</code></td>
<td><code>bytes</code></td>
<td>
</td>
<td>
No
</td>
</tr>
<tr id="DeclarePropertyArguments-span">
<td><code>span</code></td>
<td><code><a href="#LifeSpan">LifeSpan</a></code></td>
<td>
</td>
<td>
No
</td>
</tr>
</tbody>
</table>
</section>
<h2 id="WasmType">WasmType</h2>
<section>
<table class="enum-values">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr id="WasmType-Bytes">
<td><code>Bytes</code></td>
<td>
</td>
</tr>
<tr id="WasmType-String">
<td><code>String</code></td>
<td>
</td>
</tr>
<tr id="WasmType-FlatBuffers">
<td><code>FlatBuffers</code></td>
<td>
</td>
</tr>
<tr id="WasmType-Protobuf">
<td><code>Protobuf</code></td>
<td>
</td>
</tr>
</tbody>
</table>
</section>
<h2 id="LifeSpan">LifeSpan</h2>
<section>
<table class="enum-values">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr id="LifeSpan-FilterChain">
<td><code>FilterChain</code></td>
<td>
</td>
</tr>
<tr id="LifeSpan-DownstreamRequest">
<td><code>DownstreamRequest</code></td>
<td>
</td>
</tr>
<tr id="LifeSpan-DownstreamConnection">
<td><code>DownstreamConnection</code></td>
<td>
</td>
</tr>
</tbody>
</table>
</section>

View File

@ -0,0 +1,42 @@
/* Copyright 2020 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.
*/
// DO NOT MODIFY.
// This is a copy of envoy-wasm proto, used for Wasm build only.
syntax = "proto3";
package envoy.source.extensions.common.wasm;
enum WasmType {
Bytes = 0;
String = 1;
FlatBuffers = 2;
Protobuf = 3;
};
enum LifeSpan {
FilterChain = 0;
DownstreamRequest = 1;
DownstreamConnection = 2;
};
message DeclarePropertyArguments {
string name = 1;
bool readonly = 2;
WasmType type = 3;
bytes schema = 4;
LifeSpan span = 5;
};

View File

@ -0,0 +1,236 @@
/* 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 "extensions/metadata_exchange/plugin.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "extensions/common/context.h"
#include "extensions/common/proto_util.h"
#include "extensions/common/util.h"
#include "extensions/common/wasm/json_util.h"
#ifndef NULL_PLUGIN
#include "extensions/common/wasm/base64.h"
#include "extensions/metadata_exchange/declare_property.pb.h"
#else
#include "source/common/common/base64.h"
#include "source/extensions/common/wasm/ext/declare_property.pb.h"
namespace proxy_wasm {
namespace null_plugin {
namespace MetadataExchange {
namespace Plugin {
PROXY_WASM_NULL_PLUGIN_REGISTRY;
using Base64 = Envoy::Base64;
#endif
static RegisterContextFactory register_MetadataExchange(CONTEXT_FACTORY(PluginContext),
ROOT_FACTORY(PluginRootContext));
void PluginRootContext::updateMetadataValue() {
auto node_info = ::Wasm::Common::extractLocalNodeFlatBuffer();
google::protobuf::Struct metadata;
::Wasm::Common::extractStructFromNodeFlatBuffer(
*flatbuffers::GetRoot<::Wasm::Common::FlatNode>(node_info.data()), &metadata);
std::string metadata_bytes;
::Wasm::Common::serializeToStringDeterministic(metadata, &metadata_bytes);
metadata_value_ = Base64::encode(metadata_bytes.data(), metadata_bytes.size());
}
// Metadata exchange has sane defaults and therefore it will be fully
// functional even with configuration errors.
// A configuration error thrown here will cause the proxy to crash.
bool PluginRootContext::onConfigure(size_t size) {
updateMetadataValue();
if (!getValue({"node", "id"}, &node_id_)) {
LOG_DEBUG("cannot get node ID");
}
LOG_DEBUG(
absl::StrCat("metadata_value_ id:", id(), " value:", metadata_value_, " node:", node_id_));
// Parse configuration JSON string.
if (size > 0 && !configure(size)) {
LOG_WARN("configuration has errrors, but initialzation can continue.");
}
// Declare filter state property type.
const std::string function = "declare_property";
envoy::source::extensions::common::wasm::DeclarePropertyArguments args;
args.set_type(envoy::source::extensions::common::wasm::WasmType::FlatBuffers);
args.set_span(envoy::source::extensions::common::wasm::LifeSpan::DownstreamRequest);
args.set_schema(::Wasm::Common::nodeInfoSchema().data(), ::Wasm::Common::nodeInfoSchema().size());
std::string in;
args.set_name(std::string(::Wasm::Common::kUpstreamMetadataKey));
args.SerializeToString(&in);
proxy_call_foreign_function(function.data(), function.size(), in.data(), in.size(), nullptr,
nullptr);
args.set_name(std::string(::Wasm::Common::kDownstreamMetadataKey));
args.SerializeToString(&in);
proxy_call_foreign_function(function.data(), function.size(), in.data(), in.size(), nullptr,
nullptr);
return true;
}
bool PluginRootContext::configure(size_t configuration_size) {
auto configuration_data =
getBufferBytes(WasmBufferType::PluginConfiguration, 0, configuration_size);
// Parse configuration JSON string.
auto result = ::Wasm::Common::JsonParse(configuration_data->view());
if (!result.has_value()) {
LOG_WARN(absl::StrCat("cannot parse plugin configuration JSON string: ",
::Wasm::Common::toAbslStringView(configuration_data->view())));
return false;
}
auto j = result.value();
auto max_peer_cache_size_field = ::Wasm::Common::JsonGetField<int64_t>(j, "max_peer_cache_size");
if (max_peer_cache_size_field.detail() == Wasm::Common::JsonParserResultDetail::OK) {
max_peer_cache_size_ = max_peer_cache_size_field.value();
}
return true;
}
bool PluginRootContext::updatePeer(std::string_view key, std::string_view peer_id,
std::string_view peer_header) {
std::string id = std::string(peer_id);
if (max_peer_cache_size_ > 0) {
auto it = cache_.find(id);
if (it != cache_.end()) {
setFilterState(key, it->second);
return true;
}
}
#ifndef NULL_PLUGIN
auto peer_header_view = peer_header;
#else
auto peer_header_view = Wasm::Common::toAbslStringView(peer_header);
#endif
auto bytes = Base64::decodeWithoutPadding(peer_header_view);
google::protobuf::Struct metadata;
if (!metadata.ParseFromString(bytes)) {
return false;
}
auto fb = ::Wasm::Common::extractNodeFlatBufferFromStruct(metadata);
std::string_view out(reinterpret_cast<const char*>(fb.data()), fb.size());
setFilterState(key, out);
if (max_peer_cache_size_ > 0) {
// do not let the cache grow beyond max cache size.
if (static_cast<uint32_t>(cache_.size()) > max_peer_cache_size_) {
auto it = cache_.begin();
cache_.erase(cache_.begin(), std::next(it, max_peer_cache_size_ / 4));
LOG_DEBUG(absl::StrCat("cleaned cache, new cache_size:", cache_.size()));
}
cache_.emplace(std::move(id), out);
}
return true;
}
FilterHeadersStatus PluginContext::onRequestHeaders(uint32_t, bool) {
// strip and store downstream peer metadata
auto downstream_metadata_id = getRequestHeader(ExchangeMetadataHeaderId);
if (downstream_metadata_id != nullptr && !downstream_metadata_id->view().empty()) {
removeRequestHeader(ExchangeMetadataHeaderId);
setFilterState(::Wasm::Common::kDownstreamMetadataIdKey, downstream_metadata_id->view());
} else {
metadata_id_received_ = false;
}
auto downstream_metadata_value = getRequestHeader(ExchangeMetadataHeader);
if (downstream_metadata_value != nullptr && !downstream_metadata_value->view().empty()) {
removeRequestHeader(ExchangeMetadataHeader);
if (!rootContext()->updatePeer(::Wasm::Common::kDownstreamMetadataKey,
downstream_metadata_id->view(),
downstream_metadata_value->view())) {
LOG_DEBUG("cannot set downstream peer node");
}
} else {
metadata_received_ = false;
}
// do not send request internal headers to sidecar app if it is an inbound
// proxy
if (direction_ != ::Wasm::Common::TrafficDirection::Inbound) {
auto metadata = metadataValue();
// insert peer metadata struct for upstream
if (!metadata.empty()) {
replaceRequestHeader(ExchangeMetadataHeader, metadata);
}
auto nodeid = nodeId();
if (!nodeid.empty()) {
replaceRequestHeader(ExchangeMetadataHeaderId, nodeid);
}
}
return FilterHeadersStatus::Continue;
}
FilterHeadersStatus PluginContext::onResponseHeaders(uint32_t, bool) {
// strip and store upstream peer metadata
auto upstream_metadata_id = getResponseHeader(ExchangeMetadataHeaderId);
if (upstream_metadata_id != nullptr && !upstream_metadata_id->view().empty()) {
removeResponseHeader(ExchangeMetadataHeaderId);
setFilterState(::Wasm::Common::kUpstreamMetadataIdKey, upstream_metadata_id->view());
}
auto upstream_metadata_value = getResponseHeader(ExchangeMetadataHeader);
if (upstream_metadata_value != nullptr && !upstream_metadata_value->view().empty()) {
removeResponseHeader(ExchangeMetadataHeader);
if (!rootContext()->updatePeer(::Wasm::Common::kUpstreamMetadataKey,
upstream_metadata_id->view(), upstream_metadata_value->view())) {
LOG_DEBUG("cannot set upstream peer node");
}
}
// do not send response internal headers to sidecar app if it is an outbound
// proxy
if (direction_ != ::Wasm::Common::TrafficDirection::Outbound) {
auto metadata = metadataValue();
// insert peer metadata struct for downstream
if (!metadata.empty() && metadata_received_) {
replaceResponseHeader(ExchangeMetadataHeader, metadata);
}
auto nodeid = nodeId();
if (!nodeid.empty() && metadata_id_received_) {
replaceResponseHeader(ExchangeMetadataHeaderId, nodeid);
}
}
return FilterHeadersStatus::Continue;
}
#ifdef NULL_PLUGIN
} // namespace Plugin
} // namespace MetadataExchange
} // namespace null_plugin
} // namespace proxy_wasm
#endif

View File

@ -0,0 +1,94 @@
/* 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 "extensions/common/context.h"
#ifndef NULL_PLUGIN
#include <assert.h>
#define ASSERT(_X) assert(_X)
#include "proxy_wasm_intrinsics.h"
static const std::string EMPTY_STRING;
#else
#include "include/proxy-wasm/null_plugin.h"
namespace proxy_wasm {
namespace null_plugin {
namespace MetadataExchange {
namespace Plugin {
#endif
constexpr std::string_view ExchangeMetadataHeader = "x-envoy-peer-metadata";
constexpr std::string_view ExchangeMetadataHeaderId = "x-envoy-peer-metadata-id";
const size_t DefaultNodeCacheMaxSize = 500;
// PluginRootContext is the root context for all streams processed by the
// thread. It has the same lifetime as the worker thread and acts as target for
// interactions that outlives individual stream, e.g. timer, async calls.
class PluginRootContext : public RootContext {
public:
PluginRootContext(uint32_t id, std::string_view root_id) : RootContext(id, root_id) {}
bool onConfigure(size_t) override;
bool configure(size_t);
std::string_view metadataValue() { return metadata_value_; };
std::string_view nodeId() { return node_id_; };
bool updatePeer(std::string_view key, std::string_view peer_id, std::string_view peer_header);
private:
void updateMetadataValue();
std::string metadata_value_;
std::string node_id_;
// maps peer ID to the decoded peer flat buffer
std::unordered_map<std::string, std::string> cache_;
int64_t max_peer_cache_size_{DefaultNodeCacheMaxSize};
};
// Per-stream context.
class PluginContext : public Context {
public:
explicit PluginContext(uint32_t id, RootContext* root) : Context(id, root) {
direction_ = ::Wasm::Common::getTrafficDirection();
}
FilterHeadersStatus onRequestHeaders(uint32_t, bool) override;
FilterHeadersStatus onResponseHeaders(uint32_t, bool) override;
private:
inline PluginRootContext* rootContext() {
return dynamic_cast<PluginRootContext*>(this->root());
};
inline std::string_view metadataValue() { return rootContext()->metadataValue(); };
inline std::string_view nodeId() { return rootContext()->nodeId(); }
::Wasm::Common::TrafficDirection direction_;
bool metadata_received_{true};
bool metadata_id_received_{true};
};
#ifdef NULL_PLUGIN
} // namespace Plugin
} // namespace MetadataExchange
} // namespace null_plugin
} // namespace proxy_wasm
#endif

View File

@ -0,0 +1,47 @@
# 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"])
load(
"@envoy//bazel:envoy_build_system.bzl",
"envoy_cc_library",
)
envoy_cc_library(
name = "stackdriver_plugin",
srcs = [
"stackdriver.cc",
"stackdriver_plugin_factory.cc",
],
hdrs = [
"stackdriver.h",
],
repository = "@envoy",
deps = [
"//extensions/common:context",
"//extensions/common:proto_util",
"//extensions/stackdriver/common:constants",
"//extensions/stackdriver/config/v1alpha1:stackdriver_plugin_config_cc_proto",
"//extensions/stackdriver/log:exporter",
"//extensions/stackdriver/log:logger",
"//extensions/stackdriver/metric",
"@io_opencensus_cpp//opencensus/exporters/stats/stackdriver:stackdriver_exporter",
"@proxy_wasm_cpp_host//:null_lib",
"@proxy_wasm_cpp_sdk//contrib:contrib_lib",
],
)

View File

@ -0,0 +1,38 @@
As part of the ongoing Telemetry V2 effort to move Mixer like processing to the proxy as Envoy filters, we are releasing experimental support for Stackdriver HTTP telemetry.
It is a replacement for the current Mixer Stackdriver adapter, which supports exporting [GCP Istio standard metric](https://cloud.google.com/monitoring/api/metrics_istio), server access log, and traces from the proxy.
## How to enable
### Metrics and Access Logs
Metrics and server side access log will be enabled by default by installing the Stackdriver filter.
1. Disable `Stackdriver adapter` or `istio-telemetry`
To avoid duplicated telemetry reporting, you should disable `Stackdriver adapter` if you already set it up with Mixer v1. To disable Stackdriver adapter, remove Stackdriver Mixer rules, handlers, and instances, e.g. run `kubectl delete -n istio-system rule your-stackdriver-rule && kubectl delete -n istio-system handler your-stackdriver-handlers && kubectl delete -n istio-system instance your-stackdriver-instance`
If you want to disable istio-telemetry as whole:
* If your cluster is installed using `istioctl`, run `istioctl manifest apply --set values.mixer.telemetry.enabled=false,values.mixer.policy.enabled=false`. If your cluster is installed via helm, run `helm template install/kubernetes/helm/istio --name istio --namespace istio-system --set mixer.telemetry.enabled=false --set mixer.policy.enabled=false`.
* Alternatively, you can comment out mixerCheckServer and mixerReportServer in your mesh configuration.
2. Enable metadata exchange filter
`kubectl -n istio-system apply -f https://raw.githubusercontent.com/istio/proxy/release-1.4/extensions/stats/testdata/istio/metadata-exchange_filter.yaml`
3. Enable Stackdriver filter
`kubectl -n istio-system apply -f https://raw.githubusercontent.com/istio/proxy/release-1.4/extensions/stackdriver/testdata/stackdriver_filter.yaml`
4. Visit Stackdriver Monitoring metric explorer and search for [standard Istio metrics](https://cloud.google.com/monitoring/api/metrics_istio). Visit Stackdriver Logging Viewer and search `server-accesslog-stackdriver` for access log entries.
### Trace
Opencensus tracer is by default shipped with 1.4.0 Istio proxy, which supports exporting traces to Stackdriver as other tracers liker Jeager and Zipkin. To enable it, set global tracer as Stackdriver: `helm template --set global.proxy.tracer="stackdriver" install/kubernetes/helm/istio --name istio --namespace istio-system | kubectl apply -f -`. The default sampling rate is 1%. To raise it, you could set it via traceSampling helm option: `--set pilot.traceSampling=100`
## Limitations in 1.4.0
1. No TCP telemetry.
2. Access log misses some labels, which will be added in the following 1.4.x releases.
## Details
1. The preview version uses the WASM sandbox API, but *does not* run inside a WASM VM. It is natively compiled in Envoy using `NullVM`.
2. In later release we will enable running filters in the V8 WASM VM.
3. At present The filters are configured using the Istio Envoy Filter API.

View File

@ -0,0 +1,83 @@
# 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.
#
################################################################################
#
licenses(["notice"])
package(default_visibility = ["//extensions/stackdriver:__subpackages__"])
load(
"@envoy//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_cc_test",
)
load(
"@envoy//test/extensions:extensions_build_system.bzl",
"envoy_extension_cc_test",
)
envoy_cc_library(
name = "constants",
hdrs = [
"constants.h",
],
repository = "@envoy",
)
envoy_cc_library(
name = "utils",
srcs = [
"utils.cc",
],
hdrs = [
"utils.h",
],
external_deps = ["grpc"],
repository = "@envoy",
deps = [
":constants",
"//extensions/common:context",
"@com_google_googleapis//google/monitoring/v3:monitoring_cc_proto",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
],
)
envoy_extension_cc_test(
name = "utils_test",
size = "small",
srcs = ["utils_test.cc"],
extension_names = ["envoy.filters.http.wasm"],
repository = "@envoy",
deps = [
":utils",
"@envoy//test/test_common:status_utility_lib",
"@envoy//test/test_common:wasm_lib",
],
)
envoy_cc_library(
name = "metrics",
srcs = [
"metrics.cc",
],
hdrs = [
"metrics.h",
],
repository = "@envoy",
deps = [
"@proxy_wasm_cpp_host//:null_lib",
],
)

View File

@ -0,0 +1,154 @@
/* 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 <string>
#include <string_view>
namespace Extensions {
namespace Stackdriver {
namespace Common {
// Measure names of metrics.
constexpr char kServerRequestCountMeasure[] = "server/request_count_measure";
constexpr char kServerRequestBytesMeasure[] = "server/request_bytes_measure";
constexpr char kServerResponseBytesMeasure[] = "server/response_bytes_measure";
constexpr char kServerResponseLatenciesMeasure[] = "server/response_latencies_measure";
constexpr char kClientRequestCountMeasure[] = "client/request_count_measure";
constexpr char kClientRequestBytesMeasure[] = "client/request_bytes_measure";
constexpr char kClientResponseBytesMeasure[] = "client/response_bytes_measure";
constexpr char kClientRoundtripLatenciesMeasure[] = "client/roundtrip_latencies_measure";
constexpr char kServerConnectionsOpenCountMeasure[] = "server/connection_open_count_measure";
constexpr char kServerConnectionsCloseCountMeasure[] = "server/connection_close_count_measure";
constexpr char kServerReceivedBytesCountMeasure[] = "server/received_bytes_count_measure";
constexpr char kServerSentBytesCountMeasure[] = "server/sent_bytes_count_measure";
constexpr char kClientConnectionsOpenCountMeasure[] = "client/connection_open_count_measure";
constexpr char kClientConnectionsCloseCountMeasure[] = "client/connection_close_count_measure";
constexpr char kClientReceivedBytesCountMeasure[] = "client/received_bytes_count_measure";
constexpr char kClientSentBytesCountMeasure[] = "client/sent_bytes_count_measure";
// View names of metrics.
constexpr char kServerRequestCountView[] = "server/request_count";
constexpr char kServerRequestBytesView[] = "server/request_bytes";
constexpr char kServerResponseBytesView[] = "server/response_bytes";
constexpr char kServerResponseLatenciesView[] = "server/response_latencies";
constexpr char kClientRequestCountView[] = "client/request_count";
constexpr char kClientRequestBytesView[] = "client/request_bytes";
constexpr char kClientResponseBytesView[] = "client/response_bytes";
constexpr char kClientRoundtripLatenciesView[] = "client/roundtrip_latencies";
constexpr char kServerConnectionsOpenCountView[] = "server/connection_open_count";
constexpr char kServerConnectionsCloseCountView[] = "server/connection_close_count";
constexpr char kServerReceivedBytesCountView[] = "server/received_bytes_count";
constexpr char kServerSentBytesCountView[] = "server/sent_bytes_count";
constexpr char kClientConnectionsOpenCountView[] = "client/connection_open_count";
constexpr char kClientConnectionsCloseCountView[] = "client/connection_close_count";
constexpr char kClientReceivedBytesCountView[] = "client/received_bytes_count";
constexpr char kClientSentBytesCountView[] = "client/sent_bytes_count";
constexpr std::string_view kDefinedLabels[] = {
"request_protocol",
"service_authentication_policy",
"mesh_uid",
"destination_service_name",
"destination_service_namespace",
"destination_port",
"source_principal",
"source_workload_name",
"source_workload_namespace",
"source_owner",
"destination_principal",
"destination_workload_name",
"destination_workload_namespace",
"destination_owner",
"source_canonical_service_name",
"destination_canonical_service_name",
"source_canonical_service_namespace",
"destination_canonical_service_namespace",
"source_canonical_revision",
"destination_canonical_revision",
};
constexpr std::string_view kHttpDefinedLabels[] = {
"request_operation",
"response_code",
"api_version",
"api_name",
};
// Prefix for Istio metrics.
constexpr char kIstioMetricPrefix[] = "istio.io/service/";
// Monitored resource
constexpr char kPodMonitoredResource[] = "k8s_pod";
constexpr char kContainerMonitoredResource[] = "k8s_container";
constexpr char kGCEInstanceMonitoredResource[] = "gce_instance";
constexpr char kGenericNode[] = "generic_node";
constexpr char kProjectIDLabel[] = "project_id";
constexpr char kLocationLabel[] = "location";
constexpr char kClusterNameLabel[] = "cluster_name";
constexpr char kNamespaceNameLabel[] = "namespace_name";
constexpr char kPodNameLabel[] = "pod_name";
constexpr char kContainerNameLabel[] = "container_name";
constexpr char kGCEInstanceIDLabel[] = "instance_id";
constexpr char kZoneLabel[] = "zone";
constexpr char kNamespaceLabel[] = "namespace"; // used for generic_node
constexpr char kNodeIDLabel[] = "node_id"; // used for generic_node
// GCP node metadata key
constexpr char kGCPLocationKey[] = "gcp_location";
constexpr char kGCPClusterNameKey[] = "gcp_gke_cluster_name";
constexpr char kGCPProjectKey[] = "gcp_project";
constexpr std::string_view kGCPProjectNumberKey = "gcp_project_number";
constexpr char kGCPGCEInstanceIDKey[] = "gcp_gce_instance_id";
constexpr std::string_view kGCECreatedByKey = "gcp_gce_instance_created_by";
// Misc
constexpr char kIstioProxyContainerName[] = "istio-proxy";
constexpr double kNanosecondsPerMillisecond = 1000000.0;
// Stackdriver root context id.
constexpr char kOutboundRootContextId[] = "stackdriver_outbound";
constexpr char kInboundRootContextId[] = "stackdriver_inbound";
// Stackdriver service endpoint node metadata key.
constexpr char kSecureStackdriverEndpointKey[] = "SECURE_STACKDRIVER_ENDPOINT";
constexpr char kInsecureStackdriverEndpointKey[] = "INSECURE_STACKDRIVER_ENDPOINT";
constexpr char kMonitoringEndpointKey[] = "STACKDRIVER_MONITORING_ENDPOINT";
constexpr char kMonitoringExportIntervalKey[] = "STACKDRIVER_MONITORING_EXPORT_INTERVAL_SECS";
constexpr char kLoggingExportIntervalKey[] = "STACKDRIVER_LOGGING_EXPORT_INTERVAL_SECS";
constexpr char kTcpLogEntryTimeoutKey[] = "STACKDRIVER_TCP_LOG_ENTRY_TIMEOUT_SECS";
constexpr char kProxyTickerIntervalKey[] = "STACKDRIVER_PROXY_TICKER_INTERVAL_SECS";
constexpr char kTokenFile[] = "STACKDRIVER_TOKEN_FILE";
constexpr char kCACertFile[] = "STACKDRIVER_ROOT_CA_FILE";
// Port of security token exchange server (STS).
constexpr char kSTSPortKey[] = "STS_PORT";
// STS credentials
constexpr char kSTSSubjectTokenPath[] = "/var/run/secrets/tokens/istio-token";
constexpr char kSTSSubjectTokenType[] = "urn:ietf:params:oauth:token-type:jwt";
constexpr char kSTSScope[] = "https://www.googleapis.com/auth/cloud-platform";
// Stackdriver services
constexpr char kMonitoringService[] = "monitoring.googleapis.com";
constexpr char kLoggingService[] = "logging.googleapis.com";
constexpr char kMeshTelemetryService[] = "meshtelemetry.googleapis.com";
const std::string kUnknownLabel = "unknown";
} // namespace Common
} // namespace Stackdriver
} // namespace Extensions

View File

@ -0,0 +1,52 @@
/* Copyright 2020 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/stackdriver/common/metrics.h"
#ifdef NULL_PLUGIN
namespace proxy_wasm {
namespace null_plugin {
#endif
namespace Extensions {
namespace Stackdriver {
namespace Common {
uint32_t newExportCallMetric(const std::string& type, bool success) {
// NOTE: export_call cannot be a static global object, because in Nullvm,
// global metric is shared by base VM and thread local VM, but at host side,
// metrics are attached to a specific VM/root context. Since (1) metric object
// keeps an internal map which records all fully resolved metrics and avoid
// define metric ABI call when the same metric are seen (2) base VM always
// initiliazing before thread local VM, sharing a global metric object between
// base VM and thread local VM would cause host side thread local VM root
// context missing metric definition. This is not going to be a problem with
// real Wasm VM due to memory isolation.
Metric export_call(MetricType::Counter, "envoy_export_call",
{MetricTag{"wasm_filter", MetricTag::TagType::String},
MetricTag{"type", MetricTag::TagType::String},
MetricTag{"success", MetricTag::TagType::Bool}});
return export_call.resolve("stackdriver_filter", type, success);
}
} // namespace Common
} // namespace Stackdriver
} // namespace Extensions
#ifdef NULL_PLUGIN
} // namespace null_plugin
} // namespace proxy_wasm
#endif

View File

@ -0,0 +1,44 @@
/* Copyright 2020 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
#ifndef NULL_PLUGIN
#include "api/wasm/cpp/proxy_wasm_intrinsics.h"
#else
#include "include/proxy-wasm/null_plugin.h"
namespace proxy_wasm {
namespace null_plugin {
#endif
namespace Extensions {
namespace Stackdriver {
namespace Common {
// newExportCallMetric create a fully resolved metric based on the given type
// and a boolean which indicates whether the call succeeds or not. Current type
// could only be logging.
uint32_t newExportCallMetric(const std::string& type, bool success);
} // namespace Common
} // namespace Stackdriver
} // namespace Extensions
#ifdef NULL_PLUGIN
} // namespace null_plugin
} // namespace proxy_wasm
#endif

View File

@ -0,0 +1,261 @@
/* 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 "extensions/stackdriver/common/utils.h"
#include "absl/strings/str_join.h"
#include "extensions/common/util.h"
#include "extensions/stackdriver/common/constants.h"
#include "grpcpp/grpcpp.h"
namespace Extensions {
namespace Stackdriver {
namespace Common {
namespace {
const std::string
getContainerName(const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>* containers) {
if (containers && containers->size() == 1) {
return flatbuffers::GetString(containers->Get(0));
}
return kIstioProxyContainerName;
}
std::string
getNodeID(const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>* ip_addrs) {
if (!ip_addrs || ip_addrs->size() == 0) {
return "istio-proxy";
}
std::vector<std::string> ips;
ips.reserve(ip_addrs->size());
for (auto it = ip_addrs->begin(); it != ip_addrs->end(); ++it) {
ips.push_back(flatbuffers::GetString(*it));
}
return absl::StrJoin(ips, ",");
}
} // namespace
using google::api::MonitoredResource;
void buildEnvoyGrpcService(const StackdriverStubOption& stub_option, GrpcService* grpc_service) {
if (!stub_option.insecure_endpoint.empty()) {
// Do not set up credential if insecure endpoint is provided. This is only
// for testing.
grpc_service->mutable_google_grpc()->set_target_uri(stub_option.insecure_endpoint);
return;
}
grpc_service->mutable_google_grpc()->set_target_uri(stub_option.secure_endpoint.empty()
? stub_option.default_endpoint
: stub_option.secure_endpoint);
if (stub_option.sts_port.empty()) {
// Security token exchange is not enabled. Use default Google credential.
grpc_service->mutable_google_grpc()->mutable_channel_credentials()->mutable_google_default();
return;
}
setSTSCallCredentialOptions(
grpc_service->mutable_google_grpc()->add_call_credentials()->mutable_sts_service(),
stub_option.sts_port,
stub_option.test_token_path.empty() ? kSTSSubjectTokenPath : stub_option.test_token_path);
auto initial_metadata = grpc_service->add_initial_metadata();
// When using p4sa/sts, google backend needs `x-goog-user-project` in initial
// metadata to differentiate which project the call should be accounted for.
initial_metadata->set_key("x-goog-user-project");
initial_metadata->set_value(stub_option.project_id);
auto* ssl_creds =
grpc_service->mutable_google_grpc()->mutable_channel_credentials()->mutable_ssl_credentials();
if (!stub_option.test_root_pem_path.empty()) {
ssl_creds->mutable_root_certs()->set_filename(stub_option.test_root_pem_path);
}
}
bool isRawGCEInstance(const ::Wasm::Common::FlatNode& node) {
auto platform_metadata = node.platform_metadata();
if (!platform_metadata) {
return false;
}
auto instance_id = platform_metadata->LookupByKey(kGCPGCEInstanceIDKey);
auto cluster_name = platform_metadata->LookupByKey(kGCPClusterNameKey);
return instance_id && !cluster_name;
}
std::string getGCEInstanceUID(const ::Wasm::Common::FlatNode& node) {
auto platform_metadata = node.platform_metadata();
if (!platform_metadata) {
return "";
}
auto project = platform_metadata->LookupByKey(kGCPProjectKey);
auto location = platform_metadata->LookupByKey(kGCPLocationKey);
auto instance_id = platform_metadata->LookupByKey(kGCPGCEInstanceIDKey);
auto name = node.name() ? node.name()->string_view() : std::string_view();
if (name.size() == 0 && instance_id) {
name = instance_id->value()->string_view();
}
if (name.size() > 0 && project && location) {
return absl::StrCat("//compute.googleapis.com/projects/",
::Wasm::Common::toAbslStringView(project->value()->string_view()),
"/zones/",
::Wasm::Common::toAbslStringView(location->value()->string_view()),
"/instances/", ::Wasm::Common::toAbslStringView(name));
}
return "";
}
std::string getOwner(const ::Wasm::Common::FlatNode& node) {
// do not override supplied owner
if (node.owner()) {
return flatbuffers::GetString(node.owner());
}
// only attempt for GCE Instances at this point. Support for other
// platforms will have to be added later. We also don't try to discover
// owners for GKE workload instances, as those should be handled by the
// sidecar injector.
if (isRawGCEInstance(node)) {
auto platform_metadata = node.platform_metadata();
if (!platform_metadata) {
return "";
}
auto created_by = platform_metadata->LookupByKey(kGCECreatedByKey.data());
if (created_by) {
return absl::StrCat("//compute.googleapis.com/",
::Wasm::Common::toAbslStringView(created_by->value()->string_view()));
}
return getGCEInstanceUID(node);
}
return "";
}
void getMonitoredResource(const std::string& monitored_resource_type,
const ::Wasm::Common::FlatNode& local_node_info,
MonitoredResource* monitored_resource) {
if (!monitored_resource) {
return;
}
monitored_resource->set_type(monitored_resource_type);
auto platform_metadata = local_node_info.platform_metadata();
if (platform_metadata) {
auto project_key = platform_metadata->LookupByKey(kGCPProjectKey);
if (project_key) {
(*monitored_resource->mutable_labels())[kProjectIDLabel] =
flatbuffers::GetString(project_key->value());
}
}
if (monitored_resource_type == kGenericNode) {
// need location, namespace, node_id
if (platform_metadata) {
auto location_label = platform_metadata->LookupByKey(kGCPLocationKey);
if (location_label) {
(*monitored_resource->mutable_labels())[kLocationLabel] =
flatbuffers::GetString(location_label->value());
}
(*monitored_resource->mutable_labels())[kNamespaceLabel] =
flatbuffers::GetString(local_node_info.namespace_());
auto node_id = getNodeID(local_node_info.instance_ips());
(*monitored_resource->mutable_labels())[kNodeIDLabel] = node_id;
}
return;
}
if (monitored_resource_type == kGCEInstanceMonitoredResource) {
// gce_instance
if (platform_metadata) {
auto instance_id_label = platform_metadata->LookupByKey(kGCPGCEInstanceIDKey);
if (instance_id_label) {
(*monitored_resource->mutable_labels())[kGCEInstanceIDLabel] =
flatbuffers::GetString(instance_id_label->value());
}
auto zone_label = platform_metadata->LookupByKey(kGCPLocationKey);
if (zone_label) {
(*monitored_resource->mutable_labels())[kZoneLabel] =
flatbuffers::GetString(zone_label->value());
}
}
} else {
// k8s_pod or k8s_container
if (platform_metadata) {
auto location_label = platform_metadata->LookupByKey(kGCPLocationKey);
if (location_label) {
(*monitored_resource->mutable_labels())[kLocationLabel] =
flatbuffers::GetString(location_label->value());
}
auto cluster_name = platform_metadata->LookupByKey(kGCPClusterNameKey);
if (cluster_name) {
(*monitored_resource->mutable_labels())[kClusterNameLabel] =
flatbuffers::GetString(cluster_name->value());
}
}
(*monitored_resource->mutable_labels())[kNamespaceNameLabel] =
flatbuffers::GetString(local_node_info.namespace_());
(*monitored_resource->mutable_labels())[kPodNameLabel] =
flatbuffers::GetString(local_node_info.name());
if (monitored_resource_type == kContainerMonitoredResource) {
// Fill in container_name of k8s_container monitored resource.
auto container = getContainerName(local_node_info.app_containers());
(*monitored_resource->mutable_labels())[kContainerNameLabel] = container;
}
}
}
void setSTSCallCredentialOptions(
::envoy::config::core::v3::GrpcService_GoogleGrpc_CallCredentials_StsService* sts_service,
const std::string& sts_port, const std::string& token_path) {
if (!sts_service) {
return;
}
sts_service->set_token_exchange_service_uri("http://localhost:" + sts_port + "/token");
sts_service->set_subject_token_path(token_path);
sts_service->set_subject_token_type(kSTSSubjectTokenType);
sts_service->set_scope(kSTSScope);
}
void setSTSCallCredentialOptions(::grpc::experimental::StsCredentialsOptions* sts_options,
const std::string& sts_port, const std::string& token_path) {
if (!sts_options) {
return;
}
sts_options->token_exchange_service_uri = "http://localhost:" + sts_port + "/token";
sts_options->subject_token_path = token_path;
sts_options->subject_token_type = kSTSSubjectTokenType;
sts_options->scope = kSTSScope;
}
const std::string& unknownIfEmpty(const std::string& val) {
if (val.empty()) {
return kUnknownLabel;
}
return val;
}
} // namespace Common
} // namespace Stackdriver
} // namespace Extensions

View File

@ -0,0 +1,87 @@
/* 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 "absl/strings/str_cat.h"
#include "extensions/common/context.h"
#include "google/api/monitored_resource.pb.h"
#include "grpcpp/grpcpp.h"
#ifndef NULL_PLUGIN
#include "api/wasm/cpp/proxy_wasm_intrinsics.h"
#else
#include "envoy/config/core/v3/grpc_service.pb.h"
using GrpcService = ::envoy::config::core::v3::GrpcService;
#endif
namespace Extensions {
namespace Stackdriver {
namespace Common {
// StackdriverStubOption includes all the configuration to construct stackdriver
// gRPC stubs.
struct StackdriverStubOption {
std::string sts_port;
std::string default_endpoint;
std::string test_token_path;
std::string test_root_pem_path;
std::string secure_endpoint;
std::string insecure_endpoint;
std::string monitoring_endpoint;
std::string project_id;
bool enable_log_compression;
};
// Build Envoy GrpcService proto based on the given stub option.
void buildEnvoyGrpcService(const StackdriverStubOption& option, GrpcService* grpc_service);
// Determines if the proxy is running directly on GCE instance (VM).
// If the proxy is running on GKE-managed VM, this will return false.
// The determination is made based on available `platform_metadata`
// for the node.
bool isRawGCEInstance(const ::Wasm::Common::FlatNode& node);
// Returns the unique identifier for a Raw GCE Instance. If the node
// is not a GCE Instance, the empty string will be returned.
std::string getGCEInstanceUID(const ::Wasm::Common::FlatNode& node);
// Returns "owner" information for a node. If that information
// has been directly set, that value is returned. If not, and the owner
// can be entirely derived from platform metadata, this will derive the
// owner. Currently, this is only supported for GCE Instances. For
// anything else, this will return the empty string.
std::string getOwner(const ::Wasm::Common::FlatNode& node);
// Gets monitored resource proto based on the type and node metadata info.
// Only two types of monitored resource could be returned: k8s_container or
// k8s_pod.
void getMonitoredResource(const std::string& monitored_resource_type,
const ::Wasm::Common::FlatNode& local_node_info,
google::api::MonitoredResource* monitored_resource);
// Set secure exchange service gRPC call credential.
void setSTSCallCredentialOptions(
::envoy::config::core::v3::GrpcService_GoogleGrpc_CallCredentials_StsService* sts_service,
const std::string& sts_port, const std::string& token_path);
void setSTSCallCredentialOptions(::grpc::experimental::StsCredentialsOptions* sts_options,
const std::string& sts_port, const std::string& token_path);
// Return unknown if the given value is empty string.
const std::string& unknownIfEmpty(const std::string& val);
} // namespace Common
} // namespace Stackdriver
} // namespace Extensions

View File

@ -0,0 +1,122 @@
/* Copyright 2020 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/stackdriver/common/utils.h"
#include "extensions/stackdriver/common/constants.h"
#include "gmock/gmock.h"
#include "google/protobuf/util/json_util.h"
#include "google/protobuf/util/message_differencer.h"
#include "test/test_common/status_utility.h"
#include "gtest/gtest.h"
namespace Extensions {
namespace Stackdriver {
namespace Common {
using google::protobuf::util::MessageDifferencer;
TEST(UtilsTest, TestEnvoyGrpcInsecure) {
GrpcService expected_envoy_grpc_service;
std::string envoy_google_grpc_json = R"({
"google_grpc": {
"target_uri": "test"
}
})";
google::protobuf::util::JsonParseOptions options;
ASSERT_OK(JsonStringToMessage(envoy_google_grpc_json, &expected_envoy_grpc_service, options));
StackdriverStubOption opt;
opt.insecure_endpoint = "test";
GrpcService envoy_grpc_service;
buildEnvoyGrpcService(opt, &envoy_grpc_service);
std::string diff;
MessageDifferencer differ;
differ.ReportDifferencesToString(&diff);
if (!differ.Compare(expected_envoy_grpc_service, envoy_grpc_service)) {
FAIL() << "unexpected envoy grpc service " << diff << "\n";
}
}
TEST(UtilsTest, TestEnvoyGrpcSTS) {
GrpcService expected_envoy_grpc_service;
std::string envoy_google_grpc_json = R"({
"google_grpc": {
"target_uri": "secure",
"channel_credentials": {
"ssl_credentials": {}
},
"call_credentials": {
"sts_service": {
"token_exchange_service_uri": "http://localhost:1234/token",
"subject_token_path": "/var/run/secrets/tokens/istio-token",
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"scope": "https://www.googleapis.com/auth/cloud-platform"
}
}
},
"initial_metadata": {
"key": "x-goog-user-project",
"value": "project"
}
})";
google::protobuf::util::JsonParseOptions options;
ASSERT_OK(JsonStringToMessage(envoy_google_grpc_json, &expected_envoy_grpc_service, options));
StackdriverStubOption opt;
opt.secure_endpoint = "secure";
opt.sts_port = "1234";
opt.project_id = "project";
GrpcService envoy_grpc_service;
buildEnvoyGrpcService(opt, &envoy_grpc_service);
std::string diff;
MessageDifferencer differ;
differ.ReportDifferencesToString(&diff);
if (!differ.Compare(expected_envoy_grpc_service, envoy_grpc_service)) {
FAIL() << "unexpected envoy grpc service " << diff << "\n";
}
}
TEST(UtilsTest, TestEnvoyGrpcDefaultCredential) {
GrpcService expected_envoy_grpc_service;
std::string envoy_google_grpc_json = R"({
"google_grpc": {
"target_uri": "secure",
"channel_credentials": {
"google_default": {}
}
}
})";
google::protobuf::util::JsonParseOptions options;
ASSERT_OK(JsonStringToMessage(envoy_google_grpc_json, &expected_envoy_grpc_service, options));
StackdriverStubOption opt;
opt.secure_endpoint = "secure";
GrpcService envoy_grpc_service;
buildEnvoyGrpcService(opt, &envoy_grpc_service);
std::string diff;
MessageDifferencer differ;
differ.ReportDifferencesToString(&diff);
if (!differ.Compare(expected_envoy_grpc_service, envoy_grpc_service)) {
FAIL() << "unexpected envoy grpc service " << diff << "\n";
}
}
} // namespace Common
} // namespace Stackdriver
} // namespace Extensions

View File

@ -0,0 +1,34 @@
# 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.
#
################################################################################
#
cc_proto_library(
name = "stackdriver_plugin_config_cc_proto",
visibility = [
"//extensions/stackdriver:__pkg__",
"//extensions/stackdriver/metric:__pkg__",
],
deps = ["stackdriver_plugin_config_proto"],
)
proto_library(
name = "stackdriver_plugin_config_proto",
srcs = ["stackdriver_plugin_config.proto"],
deps = [
"@com_google_protobuf//:duration_proto",
"@com_google_protobuf//:wrappers_proto",
],
)

View File

@ -0,0 +1,365 @@
---
title: Stackdriver Config
description: Configuration for Stackdriver filter.
location: https://istio.io/docs/reference/config/proxy_extensions/stackdriver.html
layout: protoc-gen-docs
generator: protoc-gen-docs
weight: 20
number_of_entries: 4
---
<h2 id="CustomConfig">CustomConfig</h2>
<section>
<p>Custom instance configuration overrides.
Provides a way to customize logs.</p>
<table class="message-fields">
<thead>
<tr>
<th>Field</th>
<th>Type</th>
<th>Description</th>
<th>Required</th>
</tr>
</thead>
<tbody>
<tr id="CustomConfig-dimensions">
<td><code>dimensions</code></td>
<td><code>map&lt;string,&nbsp;string&gt;</code></td>
<td>
<p>(Optional) Collection of tag names and tag expressions to include in the
instance. Conflicts are resolved by the tag name by overriding previously
supplied values.</p>
</td>
<td>
No
</td>
</tr>
</tbody>
</table>
</section>
<h2 id="PluginConfig">PluginConfig</h2>
<section>
<p>next id: 17</p>
<table class="message-fields">
<thead>
<tr>
<th>Field</th>
<th>Type</th>
<th>Description</th>
<th>Required</th>
</tr>
</thead>
<tbody>
<tr id="PluginConfig-max_log_batch_size_in_bytes">
<td><code>max_log_batch_size_in_bytes</code></td>
<td><code>int32</code></td>
<td>
<p>Optional. Allows configuration of the size of the LogWrite request. The
size is in bytes, so that it allows for better performance. Default is 4MB.
The size of one log entry within LogWrite request is approx 1Kb.</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-log_report_duration">
<td><code>log_report_duration</code></td>
<td><code><a href="https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#duration">Duration</a></code></td>
<td>
<p>Optional. Allows configuration of the time between calls out to the
stackdriver logging service to report buffered LogWrite request.
Customers can choose to report more aggressively by keeping shorter report
interval if needed. Default is 10s.</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-enable_audit_log">
<td><code>enable_audit_log</code></td>
<td><code>bool</code></td>
<td>
<p>Optional. Controls whether to export audit log.</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-destination_service_name">
<td><code>destination_service_name</code></td>
<td><code>string</code></td>
<td>
<p>Optional. FQDN of destination service that the request routed to, e.g.
productpage.default.svc.cluster.local. If not provided, request host header
will be used instead</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-max_peer_cache_size">
<td><code>max_peer_cache_size</code></td>
<td><code>int32</code></td>
<td>
<p>maximum size of the peer metadata cache.
A long lived proxy that connects with many transient peers can build up a
large cache. To turn off the cache, set this field to a negative value.</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-disable_host_header_fallback">
<td><code>disable_host_header_fallback</code></td>
<td><code>bool</code></td>
<td>
<p>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.</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-max_edges_batch_size">
<td><code>max_edges_batch_size</code></td>
<td><code>int32</code></td>
<td>
<p>Optional. Allows configuration of the number of traffic assertions to batch
into a single request. Default is 100. Max is 1000.</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-enable_log_compression">
<td><code>enable_log_compression</code></td>
<td><code><a href="https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#boolvalue">BoolValue</a></code></td>
<td>
<p>Optional. Allows enabling log compression for stackdriver access logs.</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-access_logging">
<td><code>access_logging</code></td>
<td><code><a href="#PluginConfig-AccessLogging">AccessLogging</a></code></td>
<td>
<p>Optional. Controls what type of logs to export.</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-access_logging_filter_expression">
<td><code>access_logging_filter_expression</code></td>
<td><code>string</code></td>
<td>
<p>CEL expression for filtering access logging. If the expression evaluates
to true, an access log entry will be generated. Otherwise, no access log
entry will be generated.
NOTE: Audit logs ignore configured filters.</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-custom_log_config">
<td><code>custom_log_config</code></td>
<td><code><a href="#CustomConfig">CustomConfig</a></code></td>
<td>
<p>(Optional) Collection of tag names and tag expressions to include in the
logs. Conflicts are resolved by the tag name by overriding previously
supplied values. Does not apply to audit logs.
See
<a href="https://istio.io/latest/docs/tasks/observability/metrics/customize-metrics/#use-expressions-for-values">https://istio.io/latest/docs/tasks/observability/metrics/customize-metrics/#use-expressions-for-values</a>
for more details about the expression language.</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-metric_expiry_duration">
<td><code>metric_expiry_duration</code></td>
<td><code><a href="https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#duration">Duration</a></code></td>
<td>
<p>Optional. Controls the metric expiry duration. If a metric time series is
not updated for the given duration, it will be purged from time series
cache as well as metric reporting. If this is not set or set to 0, time
series will never be expired. This option is useful to avoid unbounded
metric label explodes proxy memory.</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-metrics_overrides">
<td><code>metrics_overrides</code></td>
<td><code>map&lt;string,&nbsp;<a href="#MetricsOverride">MetricsOverride</a>&gt;</code></td>
<td>
<p>Optional. Allows altering metrics behavior.
Metric names for specifying overloads drop the <code>istio.io/service</code> prefix.
Examples: <code>server/request_count</code>, <code>client/roundtrip_latencies</code></p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-disable_server_access_logging" class="deprecated ">
<td><code>disable_server_access_logging</code></td>
<td><code>bool</code></td>
<td>
<p>Optional. Controls whether to export server access log.
This is deprecated in favor of AccessLogging enum.</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-enable_mesh_edges_reporting" class="deprecated ">
<td><code>enable_mesh_edges_reporting</code></td>
<td><code>bool</code></td>
<td>
<p>Optional. Controls whether or not to export mesh edges to a mesh edges
service. This is disabled by default.
Deprecated &ndash; Mesh edge reporting is no longer supported and this setting
is no-op.</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-mesh_edges_reporting_duration" class="deprecated ">
<td><code>mesh_edges_reporting_duration</code></td>
<td><code><a href="https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#duration">Duration</a></code></td>
<td>
<p>Optional. Allows configuration of the time between calls out to the mesh
edges service to report <em>NEW</em> edges. The minimum configurable duration is
<code>10s</code>. NOTE: This option ONLY configures the intermediate reporting of
novel edges. Once every <code>10m</code>, all edges observed in that 10m window are
reported and the local cache is cleared.
The default duration is <code>1m</code>. Any value greater than <code>10m</code> will result in
reporting every <code>10m</code>.
Deprecated &ndash; Mesh edge reporting is no longer supported and this setting
is no-op.</p>
</td>
<td>
No
</td>
</tr>
<tr id="PluginConfig-disable_http_size_metrics" class="deprecated ">
<td><code>disable_http_size_metrics</code></td>
<td><code>bool</code></td>
<td>
<p>Optional. Allows disabling of reporting of the request and response size
metrics for HTTP traffic. Defaults to false (request and response size
metrics are enabled).
Deprecated &ndash; use <code>metrics_overrides</code> instead.
if <code>metrics_overrides</code> is used, this value will be ignored.</p>
</td>
<td>
No
</td>
</tr>
</tbody>
</table>
</section>
<h2 id="MetricsOverride">MetricsOverride</h2>
<section>
<p>Provides behavior modifications for Cloud Monitoring metrics.</p>
<table class="message-fields">
<thead>
<tr>
<th>Field</th>
<th>Type</th>
<th>Description</th>
<th>Required</th>
</tr>
</thead>
<tbody>
<tr id="MetricsOverride-drop">
<td><code>drop</code></td>
<td><code>bool</code></td>
<td>
<p>Optional. If true, no data for the associated metric will be collected or
exported.</p>
</td>
<td>
No
</td>
</tr>
<tr id="MetricsOverride-tag_overrides">
<td><code>tag_overrides</code></td>
<td><code>map&lt;string,&nbsp;string&gt;</code></td>
<td>
<p>Optional. Maps tag names to value expressions that will be used at
reporting time. If the tag name does not match a well-known tag for the
istio Cloud Monitoring metrics, the configuration will have no effect.</p>
</td>
<td>
No
</td>
</tr>
</tbody>
</table>
</section>
<h2 id="PluginConfig-AccessLogging">PluginConfig.AccessLogging</h2>
<section>
<p>Types of Access logs to export. Does not affect audit logging.</p>
<table class="enum-values">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr id="PluginConfig-AccessLogging-NONE">
<td><code>NONE</code></td>
<td>
<p>No Logs.</p>
</td>
</tr>
<tr id="PluginConfig-AccessLogging-FULL">
<td><code>FULL</code></td>
<td>
<p>All logs including both success and error logs.</p>
</td>
</tr>
<tr id="PluginConfig-AccessLogging-ERRORS_ONLY">
<td><code>ERRORS_ONLY</code></td>
<td>
<p>All error logs. This is currently only available for outbound/client side
logs. A request is classified as error when <code>status&gt;=400 or response_flag != &quot;-&quot;</code></p>
</td>
</tr>
</tbody>
</table>
</section>

View File

@ -0,0 +1,162 @@
/* 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";
// clang-format off
// $title: Stackdriver Config
// $description: Configuration for Stackdriver filter.
// $location: https://istio.io/docs/reference/config/proxy_extensions/stackdriver.html
// $weight: 20
// clang-format on
package stackdriver.config.v1alpha1;
import "google/protobuf/duration.proto";
import "google/protobuf/wrappers.proto";
// Custom instance configuration overrides.
// Provides a way to customize logs.
message CustomConfig {
// (Optional) Collection of tag names and tag expressions to include in the
// instance. Conflicts are resolved by the tag name by overriding previously
// supplied values.
map<string, string> dimensions = 1;
// (Optional) A list of tags to remove.
// Not implemented yet.
// $hide_from_docs
repeated string tags_to_remove = 2;
}
// next id: 17
message PluginConfig {
// Types of Access logs to export. Does not affect audit logging.
enum AccessLogging {
// No Logs.
NONE = 0;
// All logs including both success and error logs.
FULL = 1;
// All error logs. This is currently only available for outbound/client side
// logs. A request is classified as error when `status>=400 or
// response_flag != "-"`
ERRORS_ONLY = 2;
};
// Optional. Controls whether to export server access log.
// This is deprecated in favor of AccessLogging enum.
bool disable_server_access_logging = 1 [deprecated = true];
// Optional. Allows configuration of the size of the LogWrite request. The
// size is in bytes, so that it allows for better performance. Default is 4MB.
// The size of one log entry within LogWrite request is approx 1Kb.
int32 max_log_batch_size_in_bytes = 12;
// Optional. Allows configuration of the time between calls out to the
// stackdriver logging service to report buffered LogWrite request.
// Customers can choose to report more aggressively by keeping shorter report
// interval if needed. Default is 10s.
google.protobuf.Duration log_report_duration = 13;
// Optional. Controls whether to export audit log.
bool enable_audit_log = 11;
// Optional. FQDN of destination service that the request routed to, e.g.
// productpage.default.svc.cluster.local. If not provided, request host header
// will be used instead
string destination_service_name = 2;
// Optional. Controls whether or not to export mesh edges to a mesh edges
// service. This is disabled by default.
// Deprecated -- Mesh edge reporting is no longer supported and this setting
// is no-op.
bool enable_mesh_edges_reporting = 3 [deprecated = true];
// Optional. Allows configuration of the time between calls out to the mesh
// edges service to report *NEW* edges. The minimum configurable duration is
// `10s`. NOTE: This option ONLY configures the intermediate reporting of
// novel edges. Once every `10m`, all edges observed in that 10m window are
// reported and the local cache is cleared.
// The default duration is `1m`. Any value greater than `10m` will result in
// reporting every `10m`.
// Deprecated -- Mesh edge reporting is no longer supported and this setting
// is no-op.
google.protobuf.Duration mesh_edges_reporting_duration = 4 [deprecated = true];
// maximum size of the peer metadata cache.
// A long lived proxy that connects with many transient peers can build up a
// large cache. To turn off the cache, set this field to a negative value.
int32 max_peer_cache_size = 5;
// 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 number of traffic assertions to batch
// into a single request. Default is 100. Max is 1000.
int32 max_edges_batch_size = 7;
// Optional. Allows disabling of reporting of the request and response size
// metrics for HTTP traffic. Defaults to false (request and response size
// metrics are enabled).
// Deprecated -- use `metrics_overrides` instead.
// if `metrics_overrides` is used, this value will be ignored.
bool disable_http_size_metrics = 8 [deprecated = true];
// Optional. Allows enabling log compression for stackdriver access logs.
google.protobuf.BoolValue enable_log_compression = 9;
// Optional. Controls what type of logs to export.
AccessLogging access_logging = 10;
// CEL expression for filtering access logging. If the expression evaluates
// to true, an access log entry will be generated. Otherwise, no access log
// entry will be generated.
// NOTE: Audit logs ignore configured filters.
string access_logging_filter_expression = 17;
// (Optional) Collection of tag names and tag expressions to include in the
// logs. Conflicts are resolved by the tag name by overriding previously
// supplied values. Does not apply to audit logs.
// See
// https://istio.io/latest/docs/tasks/observability/metrics/customize-metrics/#use-expressions-for-values
// for more details about the expression language.
CustomConfig custom_log_config = 14;
// Optional. Controls the metric expiry duration. If a metric time series is
// not updated for the given duration, it will be purged from time series
// cache as well as metric reporting. If this is not set or set to 0, time
// series will never be expired. This option is useful to avoid unbounded
// metric label explodes proxy memory.
google.protobuf.Duration metric_expiry_duration = 15;
// Optional. Allows altering metrics behavior.
// Metric names for specifying overloads drop the `istio.io/service` prefix.
// Examples: `server/request_count`, `client/roundtrip_latencies`
map<string, MetricsOverride> metrics_overrides = 16;
}
// Provides behavior modifications for Cloud Monitoring metrics.
message MetricsOverride {
// Optional. If true, no data for the associated metric will be collected or
// exported.
bool drop = 1;
// Optional. Maps tag names to value expressions that will be used at
// reporting time. If the tag name does not match a well-known tag for the
// istio Cloud Monitoring metrics, the configuration will have no effect.
map<string, string> tag_overrides = 2;
}

View File

@ -0,0 +1,82 @@
# 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.
#
################################################################################
#
licenses(["notice"]) # Apache 2
load(
"@envoy//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_cc_test",
)
load(
"@envoy//test/extensions:extensions_build_system.bzl",
"envoy_extension_cc_test",
)
envoy_cc_library(
name = "logger",
srcs = [
"logger.cc",
],
hdrs = [
"logger.h",
],
repository = "@envoy",
visibility = [
"//extensions/stackdriver:__pkg__",
],
deps = [
":exporter",
"//extensions/common:context",
"//extensions/stackdriver/common:constants",
"//extensions/stackdriver/common:utils",
],
)
envoy_cc_library(
name = "exporter",
srcs = [
"exporter.cc",
],
hdrs = [
"exporter.h",
],
copts = ["-DPROXY_WASM_PROTOBUF=1"],
repository = "@envoy",
visibility = [
"//extensions/stackdriver:__pkg__",
],
deps = [
"//extensions/stackdriver/common:metrics",
"//extensions/stackdriver/common:utils",
"@com_google_googleapis//google/logging/v2:logging_cc_proto",
"@proxy_wasm_cpp_host//:null_lib",
],
)
envoy_extension_cc_test(
name = "logger_test",
size = "small",
srcs = ["logger_test.cc"],
extension_names = ["envoy.filters.http.wasm"],
repository = "@envoy",
deps = [
":logger",
"@envoy//test/test_common:status_utility_lib",
"@envoy//test/test_common:wasm_lib",
],
)

View File

@ -0,0 +1,109 @@
/* 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 "extensions/stackdriver/log/exporter.h"
#include "extensions/stackdriver/common/constants.h"
#include "extensions/stackdriver/common/metrics.h"
#ifdef NULL_PLUGIN
#include "envoy/config/core/v3/grpc_service.pb.h"
namespace proxy_wasm {
namespace null_plugin {
using envoy::config::core::v3::GrpcService;
#endif
constexpr char kGoogleLoggingService[] = "google.logging.v2.LoggingServiceV2";
constexpr char kGoogleWriteLogEntriesMethod[] = "WriteLogEntries";
constexpr int kDefaultTimeoutMillisecond = 10000;
namespace Extensions {
namespace Stackdriver {
namespace Log {
ExporterImpl::ExporterImpl(
RootContext* root_context,
const ::Extensions::Stackdriver::Common::StackdriverStubOption& stub_option) {
context_ = root_context;
auto success_counter = Common::newExportCallMetric("logging", true);
auto failure_counter = Common::newExportCallMetric("logging", false);
success_callback_ = [this, success_counter](size_t) {
incrementMetric(success_counter, 1);
LOG_DEBUG("successfully sent Stackdriver logging request");
in_flight_export_call_ -= 1;
if (in_flight_export_call_ < 0) {
LOG_WARN("in flight report call should not be negative");
}
if (in_flight_export_call_ <= 0 && is_on_done_) {
proxy_done();
}
};
failure_callback_ = [this, failure_counter](GrpcStatus status) {
// TODO(bianpengyuan): add retry.
incrementMetric(failure_counter, 1);
LOG_WARN("Stackdriver logging api call error: " + std::to_string(static_cast<int>(status)) +
getStatus().second->toString());
in_flight_export_call_ -= 1;
if (in_flight_export_call_ < 0) {
LOG_WARN("in flight report call should not be negative");
}
if (in_flight_export_call_ <= 0 && is_on_done_) {
proxy_done();
}
};
// Construct grpc_service for the Stackdriver gRPC call.
GrpcService grpc_service;
grpc_service.mutable_google_grpc()->set_stat_prefix("stackdriver_logging");
if (stub_option.enable_log_compression) {
(*grpc_service.mutable_google_grpc()
->mutable_channel_args()
->mutable_args())[GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM]
.set_int_value(GRPC_COMPRESS_GZIP);
}
buildEnvoyGrpcService(stub_option, &grpc_service);
grpc_service.SerializeToString(&grpc_service_string_);
}
void ExporterImpl::exportLogs(
const std::vector<std::unique_ptr<const google::logging::v2::WriteLogEntriesRequest>>& requests,
bool is_on_done) {
is_on_done_ = is_on_done;
HeaderStringPairs initial_metadata;
for (const auto& req : requests) {
auto result = context_->grpcSimpleCall(
grpc_service_string_, kGoogleLoggingService, kGoogleWriteLogEntriesMethod, initial_metadata,
*req, kDefaultTimeoutMillisecond, success_callback_, failure_callback_);
if (result != WasmResult::Ok) {
LOG_WARN("failed to make stackdriver logging export call");
break;
}
in_flight_export_call_ += 1;
}
}
} // namespace Log
} // namespace Stackdriver
} // namespace Extensions
#ifdef NULL_PLUGIN
} // namespace null_plugin
} // namespace proxy_wasm
#endif

View File

@ -0,0 +1,90 @@
/* 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 <string>
#include "extensions/stackdriver/common/utils.h"
#include "google/logging/v2/logging.pb.h"
#ifndef NULL_PLUGIN
#include "api/wasm/cpp/proxy_wasm_intrinsics.h"
#else
#include "include/proxy-wasm/null_plugin.h"
namespace proxy_wasm {
namespace null_plugin {
#endif
namespace Extensions {
namespace Stackdriver {
namespace Log {
// Log exporter interface.
class Exporter {
public:
virtual ~Exporter() {}
virtual void
exportLogs(const std::vector<std::unique_ptr<const google::logging::v2::WriteLogEntriesRequest>>&,
bool is_on_done) = 0;
};
// Exporter writes Stackdriver access log to the backend. It uses WebAssembly
// gRPC API.
class ExporterImpl : public Exporter {
public:
// root_context is the wasm runtime context that this instance runs with.
// logging_service_endpoint is an optional param which should be used for test
// only.
ExporterImpl(RootContext* root_context,
const ::Extensions::Stackdriver::Common::StackdriverStubOption& stub_option);
// exportLogs exports the given log request to Stackdriver.
void exportLogs(
const std::vector<std::unique_ptr<const google::logging::v2::WriteLogEntriesRequest>>& req,
bool is_on_done) override;
private:
// Wasm context that outbound calls are attached to.
RootContext* context_ = nullptr;
// Serialized string of Stackdriver logging service
std::string grpc_service_string_;
// Indicates if the current exporting is triggered by root context onDone. If
// this is true, gRPC callback needs to call proxy_done to indicate that async
// call finishes.
bool is_on_done_ = false;
// Callbacks for gRPC calls.
std::function<void(size_t)> success_callback_;
std::function<void(GrpcStatus)> failure_callback_;
// Record in flight export calls. When ondone is triggered, export call needs
// to be zero before calling proxy_done.
int in_flight_export_call_ = 0;
};
} // namespace Log
} // namespace Stackdriver
} // namespace Extensions
#ifdef NULL_PLUGIN
} // namespace null_plugin
} // namespace proxy_wasm
#endif

View File

@ -0,0 +1,472 @@
/* 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 "extensions/stackdriver/log/logger.h"
#include "absl/strings/match.h"
#include "extensions/common/util.h"
#include "extensions/stackdriver/common/constants.h"
#include "google/logging/v2/log_entry.pb.h"
#include "google/protobuf/util/time_util.h"
#include "re2/re2.h"
#ifndef NULL_PLUGIN
#include "api/wasm/cpp/proxy_wasm_intrinsics.h"
#else
#include "include/proxy-wasm/null_plugin.h"
#endif
namespace Extensions {
namespace Stackdriver {
namespace Log {
namespace {
// Matches Rbac Access denied string.
// It is of the format:
// "rbac_access_denied_matched_policy[ns[NAMESPACE]-policy[POLICY]-rule[POLICY_INDEX]]"
const RE2
rbac_denied_match("rbac_access_denied_matched_policy\\[ns\\[(.*)\\]-policy\\[(.*)\\]-rule\\[("
".*)\\]\\]");
constexpr char rbac_denied_match_prefix[] = "rbac_access_denied_matched_policy";
constexpr char kRbacAccessDenied[] = "AuthzDenied";
void setSourceCanonicalService(const ::Wasm::Common::FlatNode& peer_node_info,
google::protobuf::Map<std::string, std::string>* label_map) {
const auto peer_labels = peer_node_info.labels();
if (peer_labels) {
auto ics_iter = peer_labels->LookupByKey(Wasm::Common::kCanonicalServiceLabelName.data());
if (ics_iter) {
(*label_map)["source_canonical_service"] = flatbuffers::GetString(ics_iter->value());
}
}
}
void setDestinationCanonicalService(const ::Wasm::Common::FlatNode& peer_node_info,
google::protobuf::Map<std::string, std::string>* label_map) {
const auto peer_labels = peer_node_info.labels();
if (peer_labels) {
auto ics_iter = peer_labels->LookupByKey(Wasm::Common::kCanonicalServiceLabelName.data());
if (ics_iter) {
(*label_map)["destination_canonical_service"] = flatbuffers::GetString(ics_iter->value());
}
}
}
// Set monitored resources derived from local node info.
void setMonitoredResource(const ::Wasm::Common::FlatNode& local_node_info,
const std::string& resource_type,
google::logging::v2::WriteLogEntriesRequest* log_entries_request) {
google::api::MonitoredResource monitored_resource;
Common::getMonitoredResource(resource_type, local_node_info, &monitored_resource);
log_entries_request->mutable_resource()->CopyFrom(monitored_resource);
}
// Helper methods to fill destination Labels. Which labels are filled depends on
// if the entry is audit or not.
void fillDestinationLabels(const ::Wasm::Common::FlatNode& destination_node_info,
google::protobuf::Map<std::string, std::string>* label_map, bool audit) {
(*label_map)["destination_workload"] =
flatbuffers::GetString(destination_node_info.workload_name());
(*label_map)["destination_namespace"] =
flatbuffers::GetString(destination_node_info.namespace_());
// Don't set if audit request
if (!audit) {
(*label_map)["destination_name"] = flatbuffers::GetString(destination_node_info.name());
}
// Add destination app and version label if exist.
const auto local_labels = destination_node_info.labels();
if (local_labels) {
auto version_iter = local_labels->LookupByKey("version");
if (version_iter && !audit) {
(*label_map)["destination_version"] = flatbuffers::GetString(version_iter->value());
}
// App label is used to correlate workload and its logs in UI.
auto app_iter = local_labels->LookupByKey("app");
if (app_iter) {
(*label_map)["destination_app"] = flatbuffers::GetString(app_iter->value());
}
if (label_map->find("destination_canonical_service") == label_map->end()) {
setDestinationCanonicalService(destination_node_info, label_map);
}
auto rev_iter =
local_labels->LookupByKey(Wasm::Common::kCanonicalServiceRevisionLabelName.data());
if (rev_iter) {
(*label_map)["destination_canonical_revision"] = flatbuffers::GetString(rev_iter->value());
}
}
}
// Helper methods to fill source Labels. The labels filled depends on whether
// the log entry is audit or not.
void fillSourceLabels(const ::Wasm::Common::FlatNode& source_node_info,
google::protobuf::Map<std::string, std::string>* label_map, bool audit) {
if (!audit) {
(*label_map)["source_name"] = flatbuffers::GetString(source_node_info.name());
}
(*label_map)["source_workload"] = flatbuffers::GetString(source_node_info.workload_name());
(*label_map)["source_namespace"] = flatbuffers::GetString(source_node_info.namespace_());
// Add destination app and version label if exist.
const auto local_labels = source_node_info.labels();
if (local_labels) {
auto version_iter = local_labels->LookupByKey("version");
if (version_iter && !audit) {
(*label_map)["source_version"] = flatbuffers::GetString(version_iter->value());
}
// App label is used to correlate workload and its logs in UI.
auto app_iter = local_labels->LookupByKey("app");
if (app_iter) {
(*label_map)["source_app"] = flatbuffers::GetString(app_iter->value());
}
if (label_map->find("source_canonical_service") == label_map->end()) {
setSourceCanonicalService(source_node_info, label_map);
}
auto rev_iter =
local_labels->LookupByKey(Wasm::Common::kCanonicalServiceRevisionLabelName.data());
if (rev_iter) {
(*label_map)["source_canonical_revision"] = flatbuffers::GetString(rev_iter->value());
}
}
}
void fillExtraLabels(const std::unordered_map<std::string, std::string>& extra_labels,
google::protobuf::Map<std::string, std::string>* label_map) {
for (const auto& extra_label : extra_labels) {
(*label_map)[extra_label.first] = extra_label.second;
}
}
bool fillAuthInfo(const std::string& response_details,
google::protobuf::Map<std::string, std::string>* label_map) {
std::string policy_name, policy_namespace, policy_rule_index;
if (absl::StartsWith(response_details, rbac_denied_match_prefix)) {
(*label_map)["response_details"] = kRbacAccessDenied;
if (RE2::PartialMatch(response_details, rbac_denied_match, &policy_namespace, &policy_name,
&policy_rule_index)) {
(*label_map)["policy_name"] = absl::StrCat(policy_namespace, ".", policy_name);
(*label_map)["policy_rule"] = policy_rule_index;
}
return true;
}
return false;
}
} // namespace
using google::protobuf::util::TimeUtil;
// Name of the server access log.
constexpr char kServerAccessLogName[] = "server-accesslog-stackdriver";
// Name of the client access log.
constexpr char kClientAccessLogName[] = "client-accesslog-stackdriver";
// Name of the server audit access log.
constexpr char kServerAuditLogName[] = "server-istio-audit-log";
// Name of the client audit access log.
constexpr char kClientAuditLogName[] = "client-istio-audit-log";
void Logger::initializeLogEntryRequest(
const flatbuffers::Vector<flatbuffers::Offset<Wasm::Common::KeyVal>>* platform_metadata,
const ::Wasm::Common::FlatNode& local_node_info,
const std::unordered_map<std::string, std::string>& extra_labels, bool outbound, bool audit) {
LogEntryType log_entry_type = GetLogEntryType(outbound, audit);
log_entries_request_map_[log_entry_type]->request =
std::make_unique<google::logging::v2::WriteLogEntriesRequest>();
log_entries_request_map_[log_entry_type]->size = 0;
auto log_entries_request = log_entries_request_map_[log_entry_type]->request.get();
const std::string& log_name = audit ? (outbound ? kClientAuditLogName : kServerAuditLogName)
: (outbound ? kClientAccessLogName : kServerAccessLogName);
log_entries_request->set_log_name("projects/" + project_id_ + "/logs/" + log_name);
std::string resource_type =
outbound ? Common::kPodMonitoredResource : Common::kContainerMonitoredResource;
const auto cluster_iter =
platform_metadata ? platform_metadata->LookupByKey(Common::kGCPClusterNameKey) : nullptr;
if (!cluster_iter) {
// if there is no cluster name, then this is not a kubernetes resource
const auto instance_iter =
platform_metadata ? platform_metadata->LookupByKey(Common::kGCPGCEInstanceIDKey) : nullptr;
const auto creator_iter = platform_metadata
? platform_metadata->LookupByKey(Common::kGCECreatedByKey.data())
: nullptr;
if (!instance_iter && !creator_iter) {
resource_type = Common::kGCEInstanceMonitoredResource;
} else {
resource_type = Common::kGenericNode;
}
}
setMonitoredResource(local_node_info, resource_type, log_entries_request);
auto label_map = log_entries_request->mutable_labels();
if (!audit) {
(*label_map)["mesh_uid"] = flatbuffers::GetString(local_node_info.mesh_id());
}
// Set common labels shared by all client entries or server entries
outbound ? fillSourceLabels(local_node_info, label_map, audit)
: fillDestinationLabels(local_node_info, label_map, audit);
if (!audit) {
fillExtraLabels(extra_labels, label_map);
}
}
Logger::Logger(const ::Wasm::Common::FlatNode& local_node_info, std::unique_ptr<Exporter> exporter,
const std::unordered_map<std::string, std::string>& extra_labels,
int log_request_size_limit) {
const auto platform_metadata = local_node_info.platform_metadata();
const auto project_iter =
platform_metadata ? platform_metadata->LookupByKey(Common::kGCPProjectKey) : nullptr;
if (project_iter) {
project_id_ = flatbuffers::GetString(project_iter->value());
}
// Initalize the current WriteLogEntriesRequest for client/server
log_entries_request_map_[LogEntryType::Client] = std::make_unique<Logger::WriteLogEntryRequest>();
initializeLogEntryRequest(platform_metadata, local_node_info, extra_labels, true /*outbound */,
false /* audit */);
log_entries_request_map_[Logger::LogEntryType::Server] =
std::make_unique<Logger::WriteLogEntryRequest>();
initializeLogEntryRequest(platform_metadata, local_node_info, extra_labels, false /* outbound */,
false /* audit */);
log_entries_request_map_[LogEntryType::ClientAudit] =
std::make_unique<Logger::WriteLogEntryRequest>();
initializeLogEntryRequest(platform_metadata, local_node_info, extra_labels, true /*outbound */,
true /* audit */);
log_entries_request_map_[Logger::LogEntryType::ServerAudit] =
std::make_unique<Logger::WriteLogEntryRequest>();
initializeLogEntryRequest(platform_metadata, local_node_info, extra_labels, false /* outbound */,
true /* audit */);
log_request_size_limit_ = log_request_size_limit;
exporter_ = std::move(exporter);
}
void Logger::addTcpLogEntry(const ::Wasm::Common::RequestInfo& request_info,
const ::Wasm::Common::FlatNode& peer_node_info,
const std::unordered_map<std::string, std::string>& extra_labels,
long int log_time, bool outbound, bool audit) {
// create a new log entry
auto* log_entries =
log_entries_request_map_[GetLogEntryType(outbound, audit)]->request->mutable_entries();
auto* new_entry = log_entries->Add();
*new_entry->mutable_timestamp() =
google::protobuf::util::TimeUtil::NanosecondsToTimestamp(log_time);
addTCPLabelsToLogEntry(request_info, peer_node_info, new_entry, outbound, audit);
fillAndFlushLogEntry(request_info, peer_node_info, extra_labels, new_entry, outbound, audit);
}
void Logger::addLogEntry(const ::Wasm::Common::RequestInfo& request_info,
const ::Wasm::Common::FlatNode& peer_node_info,
const std::unordered_map<std::string, std::string>& extra_labels,
bool outbound, bool audit) {
// create a new log entry
auto* log_entries =
log_entries_request_map_[GetLogEntryType(outbound, audit)]->request->mutable_entries();
auto* new_entry = log_entries->Add();
*new_entry->mutable_timestamp() =
google::protobuf::util::TimeUtil::NanosecondsToTimestamp(request_info.start_time);
fillHTTPRequestInLogEntry(request_info, new_entry);
fillAndFlushLogEntry(request_info, peer_node_info, extra_labels, new_entry, outbound, audit);
}
void Logger::fillAndFlushLogEntry(const ::Wasm::Common::RequestInfo& request_info,
const ::Wasm::Common::FlatNode& peer_node_info,
const std::unordered_map<std::string, std::string>& extra_labels,
google::logging::v2::LogEntry* new_entry, bool outbound,
bool audit) {
// match logic from stackdriver.cc that determines if error-only logging.
if (request_info.response_code >= 400 || request_info.response_flag != ::Wasm::Common::NONE) {
new_entry->set_severity(::google::logging::type::ERROR);
} else {
new_entry->set_severity(::google::logging::type::INFO);
}
auto label_map = new_entry->mutable_labels();
if (outbound) {
fillDestinationLabels(peer_node_info, label_map, audit);
} else {
fillSourceLabels(peer_node_info, label_map, audit);
}
(*label_map)["destination_service_host"] = request_info.destination_service_host;
(*label_map)["destination_service_name"] = request_info.destination_service_name;
(*label_map)["destination_principal"] = request_info.destination_principal;
(*label_map)["source_principal"] = request_info.source_principal;
if (!audit) {
(*label_map)["response_flag"] = request_info.response_flag;
(*label_map)["service_authentication_policy"] =
std::string(::Wasm::Common::AuthenticationPolicyString(request_info.service_auth_policy));
(*label_map)["protocol"] = ::Wasm::Common::ProtocolString(request_info.request_protocol);
(*label_map)["log_sampled"] = request_info.log_sampled ? "true" : "false";
(*label_map)["connection_id"] = std::to_string(request_info.connection_id);
if (!request_info.route_name.empty()) {
(*label_map)["route_name"] = request_info.route_name;
}
if (!request_info.upstream_host.empty()) {
(*label_map)["upstream_host"] = request_info.upstream_host;
}
(*label_map)["upstream_cluster"] = request_info.upstream_cluster;
if (!request_info.requested_server_name.empty()) {
(*label_map)["requested_server_name"] = request_info.requested_server_name;
}
if (!request_info.x_envoy_original_path.empty()) {
(*label_map)["x-envoy-original-path"] = request_info.x_envoy_original_path;
}
if (!request_info.x_envoy_original_dst_host.empty()) {
(*label_map)["x-envoy-original-dst-host"] = request_info.x_envoy_original_dst_host;
}
if (!request_info.upstream_transport_failure_reason.empty()) {
(*label_map)["upstream_transport_failure_reason"] =
request_info.upstream_transport_failure_reason;
}
if (!request_info.response_details.empty()) {
if (!fillAuthInfo(request_info.response_details, label_map)) {
(*label_map)["response_details"] = request_info.response_details;
}
}
}
// Insert trace headers, if exist.
if (request_info.b3_trace_sampled) {
new_entry->set_trace("projects/" + project_id_ + "/traces/" + request_info.b3_trace_id);
new_entry->set_span_id(request_info.b3_span_id);
new_entry->set_trace_sampled(request_info.b3_trace_sampled);
}
// This is done just before flushing, so that any customized label entry can
// override existing ones.
if (!audit) {
fillExtraLabels(extra_labels, new_entry->mutable_labels());
}
LogEntryType log_entry_type = GetLogEntryType(outbound, audit);
// Accumulate estimated size of the request. If the current request exceeds
// the size limit, flush the request out.
log_entries_request_map_[log_entry_type]->size += new_entry->ByteSizeLong();
if (log_entries_request_map_[log_entry_type]->size > log_request_size_limit_) {
flush(log_entry_type);
}
}
void Logger::flush(LogEntryType log_entry_type) {
auto request = log_entries_request_map_[log_entry_type]->request.get();
std::unique_ptr<google::logging::v2::WriteLogEntriesRequest> cur =
std::make_unique<google::logging::v2::WriteLogEntriesRequest>();
cur->set_log_name(request->log_name());
cur->mutable_resource()->CopyFrom(request->resource());
*cur->mutable_labels() = request->labels();
// Swap the new request with the old one and export it.
log_entries_request_map_[log_entry_type]->request.swap(cur);
request_queue_.emplace_back(std::move(cur));
// Reset size counter.
log_entries_request_map_[log_entry_type]->size = 0;
}
bool Logger::flush() {
bool flushed = false;
// This flush is triggered by timer, thus iterate through the map to see if
// any log entry is non empty.
for (auto const& log_entry : log_entries_request_map_) {
if (log_entry.second->size != 0) {
flush(log_entry.first);
flushed = true;
}
}
return flushed;
}
bool Logger::exportLogEntry(bool is_on_done) {
if (!flush() && request_queue_.empty()) {
// No log entry needs to export.
return false;
}
exporter_->exportLogs(request_queue_, is_on_done);
request_queue_.clear();
return true;
}
void Logger::addTCPLabelsToLogEntry(const ::Wasm::Common::RequestInfo& request_info,
const ::Wasm::Common::FlatNode& peer_node_info,
google::logging::v2::LogEntry* log_entry, bool outbound,
bool audit) {
const auto& entries_request = log_entries_request_map_[GetLogEntryType(outbound, audit)]->request;
auto label_map = log_entry->mutable_labels();
std::string source, destination;
if (outbound) {
setDestinationCanonicalService(peer_node_info, label_map);
auto source_cs_iter = entries_request->labels().find("source_canonical_service");
auto destination_cs_iter = label_map->find("destination_canonical_service");
source = source_cs_iter != entries_request->labels().end()
? source_cs_iter->second
: entries_request->labels().at("source_workload");
destination = destination_cs_iter != label_map->end() ? destination_cs_iter->second
: request_info.destination_service_name;
} else {
setSourceCanonicalService(peer_node_info, label_map);
auto source_cs_iter = label_map->find("source_canonical_service");
auto destination_cs_iter = entries_request->labels().find("destination_canonical_service");
source = source_cs_iter != label_map->end()
? source_cs_iter->second
: flatbuffers::GetString(peer_node_info.workload_name());
destination = destination_cs_iter != entries_request->labels().end()
? destination_cs_iter->second
: request_info.destination_service_name;
}
log_entry->set_text_payload(absl::StrCat(source, " --> ", destination));
(*label_map)["source_ip"] = request_info.source_address;
(*label_map)["destination_ip"] = request_info.destination_address;
(*label_map)["source_port"] = std::to_string(request_info.source_port);
(*label_map)["destination_port"] = std::to_string(request_info.destination_port);
(*label_map)["total_sent_bytes"] = std::to_string(request_info.tcp_total_sent_bytes);
(*label_map)["total_received_bytes"] = std::to_string(request_info.tcp_total_received_bytes);
(*label_map)["connection_state"] =
std::string(::Wasm::Common::TCPConnectionStateString(request_info.tcp_connection_state));
}
void Logger::fillHTTPRequestInLogEntry(const ::Wasm::Common::RequestInfo& request_info,
google::logging::v2::LogEntry* log_entry) {
auto http_request = log_entry->mutable_http_request();
http_request->set_request_method(request_info.request_operation);
http_request->set_request_url(request_info.url_scheme + "://" + request_info.url_host +
request_info.path);
http_request->set_request_size(request_info.request_size);
http_request->set_status(request_info.response_code);
http_request->set_response_size(request_info.response_size);
http_request->set_user_agent(request_info.user_agent);
http_request->set_remote_ip(request_info.source_address);
http_request->set_server_ip(request_info.destination_address);
http_request->set_protocol(::Wasm::Common::ProtocolString(request_info.request_protocol).data());
*http_request->mutable_latency() =
google::protobuf::util::TimeUtil::NanosecondsToDuration(request_info.duration);
http_request->set_referer(request_info.referer);
auto label_map = log_entry->mutable_labels();
(*label_map)["request_id"] = request_info.request_id;
}
} // namespace Log
} // namespace Stackdriver
} // namespace Extensions

View File

@ -0,0 +1,164 @@
/* 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 <string>
#include <vector>
#include "extensions/common/context.h"
#include "extensions/stackdriver/log/exporter.h"
#include "google/logging/v2/logging.pb.h"
namespace Extensions {
namespace Stackdriver {
namespace Log {
#ifdef NULL_PLUGIN
using proxy_wasm::null_plugin::Extensions::Stackdriver::Log::Exporter;
#endif
// Logger records access logs and exports them to Stackdriver.
class Logger {
public:
// Logger initiate a Stackdriver access logger, which batches log entries and
// exports to Stackdriver backend with the given exporter.
// log_request_size_limit is the size limit of a logging request:
// https://cloud.google.com/logging/quotas.
Logger(const ::Wasm::Common::FlatNode& local_node_info, std::unique_ptr<Exporter> exporter,
const std::unordered_map<std::string, std::string>& extra_labels,
int log_request_size_limit = 4000000 /* 4 Mb */);
// Type of log entry.
enum LogEntryType { Client, ClientAudit, Server, ServerAudit };
// Add a new log entry based on the given request information and peer node
// information. The type of entry that is added depends on outbound and audit
// arguments.
//
// Audit labels:
// - destination_canonical_revision
// - destination_canonical_service
// - destination_service_name
// - destination_namespace
// - destination_principal
// - destination_service_host
// - destination_app
// - destination_workload
// - request_id
// - source_app
// - source_canonical_revision
// - source_canonical_service
// - source_namespace
// - source_workload
// - source_principal
//
void addLogEntry(const ::Wasm::Common::RequestInfo& request_info,
const ::Wasm::Common::FlatNode& peer_node_info,
const std::unordered_map<std::string, std::string>& extra_labels, bool outbound,
bool audit);
// Add a new tcp log entry based on the given request information and peer
// node information. The type of entry that is added depends on outbound and
// audit arguments.
void addTcpLogEntry(const ::Wasm::Common::RequestInfo& request_info,
const ::Wasm::Common::FlatNode& peer_node_info,
const std::unordered_map<std::string, std::string>& extra_labels,
long int log_time, bool outbound, bool audit);
// Export and clean the buffered WriteLogEntriesRequests. Returns true if
// async call is made to export log entry, otherwise returns false if nothing
// exported.
bool exportLogEntry(bool is_on_done);
private:
// Stores log entry request and it's size.
struct WriteLogEntryRequest {
// Request that the new log entry should be written into.
std::unique_ptr<google::logging::v2::WriteLogEntriesRequest> request;
// Estimated size of the current WriteLogEntriesRequest.
int size;
};
// Flush rotates the current WriteLogEntriesRequest. This will be triggered
// either by a timer or by request size limit. Returns false if there is no
// log entry to be exported.
bool flush();
void flush(LogEntryType log_entry_type);
// Add TCP Specific labels to LogEntry. Which labels are set depends on if
// the entry is an audit entry or not
void addTCPLabelsToLogEntry(const ::Wasm::Common::RequestInfo& request_info,
const ::Wasm::Common::FlatNode& peer_node_info,
google::logging::v2::LogEntry* log_entry, bool outbound, bool audit);
// Fill Http_Request entry in LogEntry.
void fillHTTPRequestInLogEntry(const ::Wasm::Common::RequestInfo& request_info,
google::logging::v2::LogEntry* log_entry);
// Generic method to fill the log entry. The WriteLogEntriesRequest
// containing the log entry is flushed if the request exceeds the configured
// maximum size. Which request should be flushed is determined by the outbound
// and audit arguments.
void fillAndFlushLogEntry(const ::Wasm::Common::RequestInfo& request_info,
const ::Wasm::Common::FlatNode& peer_node_info,
const std::unordered_map<std::string, std::string>& extra_labels,
google::logging::v2::LogEntry* new_entry, bool outbound, bool audit);
// Helper method to initialize log entry request. The type of log entry is
// determined by the oubound and audit arguments.
void initializeLogEntryRequest(
const flatbuffers::Vector<flatbuffers::Offset<Wasm::Common::KeyVal>>* platform_metadata,
const ::Wasm::Common::FlatNode& local_node_info,
const std::unordered_map<std::string, std::string>& extra_labels, bool outbound, bool audit);
// Helper method to get Log Entry Type.
Logger::LogEntryType GetLogEntryType(bool outbound, bool audit) const {
if (outbound) {
if (audit) {
return Logger::LogEntryType::ClientAudit;
}
return Logger::LogEntryType::Client;
}
if (audit) {
return Logger::LogEntryType::ServerAudit;
}
return Logger::LogEntryType::Server;
}
// Buffer for WriteLogEntriesRequests that are to be exported.
std::vector<std::unique_ptr<const google::logging::v2::WriteLogEntriesRequest>> request_queue_;
// Stores client/server requests that the new log entry should be written
// into.
std::unordered_map<Logger::LogEntryType, std::unique_ptr<Logger::WriteLogEntryRequest>>
log_entries_request_map_;
// Size limit of a WriteLogEntriesRequest. If current WriteLogEntriesRequest
// exceeds this size limit, flush() will be triggered.
int log_request_size_limit_;
// Exporter calls Stackdriver services to export access logs.
std::unique_ptr<Exporter> exporter_;
// GCP project that this proxy runs with.
std::string project_id_;
};
} // namespace Log
} // namespace Stackdriver
} // namespace Extensions

View File

@ -0,0 +1,457 @@
/* 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 "extensions/stackdriver/log/logger.h"
#include <memory>
#include "extensions/stackdriver/common/constants.h"
#include "extensions/stackdriver/common/utils.h"
#include "gmock/gmock.h"
#include "google/logging/v2/log_entry.pb.h"
#include "google/protobuf/util/json_util.h"
#include "google/protobuf/util/message_differencer.h"
#include "google/protobuf/util/time_util.h"
#include "test/test_common/status_utility.h"
#include "gtest/gtest.h"
namespace Extensions {
namespace Stackdriver {
namespace Log {
using google::protobuf::util::MessageDifferencer;
using google::protobuf::util::TimeUtil;
namespace {
class MockExporter : public Exporter {
public:
MOCK_METHOD2(
exportLogs,
void(const std::vector<std::unique_ptr<const google::logging::v2::WriteLogEntriesRequest>>&,
bool));
};
const ::Wasm::Common::FlatNode& nodeInfo(flatbuffers::FlatBufferBuilder& fbb) {
auto name = fbb.CreateString("test_pod");
auto namespace_ = fbb.CreateString("test_namespace");
auto workload_name = fbb.CreateString("test_workload");
auto mesh_id = fbb.CreateString("mesh");
std::vector<flatbuffers::Offset<::Wasm::Common::KeyVal>> platform_metadata = {
::Wasm::Common::CreateKeyVal(fbb, fbb.CreateString(Common::kGCPProjectKey),
fbb.CreateString("test_project")),
::Wasm::Common::CreateKeyVal(fbb, fbb.CreateString(Common::kGCPClusterNameKey),
fbb.CreateString("test_cluster")),
::Wasm::Common::CreateKeyVal(fbb, fbb.CreateString(Common::kGCPLocationKey),
fbb.CreateString("test_location"))};
auto platform_metadata_offset = fbb.CreateVectorOfSortedTables(&platform_metadata);
::Wasm::Common::FlatNodeBuilder node(fbb);
node.add_name(name);
node.add_namespace_(namespace_);
node.add_workload_name(workload_name);
node.add_mesh_id(mesh_id);
node.add_platform_metadata(platform_metadata_offset);
auto data = node.Finish();
fbb.Finish(data);
return *flatbuffers::GetRoot<::Wasm::Common::FlatNode>(fbb.GetBufferPointer());
}
const ::Wasm::Common::FlatNode& peerNodeInfo(flatbuffers::FlatBufferBuilder& fbb) {
auto name = fbb.CreateString("test_peer_pod");
auto namespace_ = fbb.CreateString("test_peer_namespace");
auto workload_name = fbb.CreateString("test_peer_workload");
std::vector<flatbuffers::Offset<::Wasm::Common::KeyVal>> platform_metadata = {
::Wasm::Common::CreateKeyVal(fbb, fbb.CreateString(Common::kGCPProjectKey),
fbb.CreateString("test_project")),
::Wasm::Common::CreateKeyVal(fbb, fbb.CreateString(Common::kGCPClusterNameKey),
fbb.CreateString("test_cluster")),
::Wasm::Common::CreateKeyVal(fbb, fbb.CreateString(Common::kGCPLocationKey),
fbb.CreateString("test_location"))};
auto platform_metadata_offset = fbb.CreateVectorOfSortedTables(&platform_metadata);
::Wasm::Common::FlatNodeBuilder node(fbb);
node.add_name(name);
node.add_namespace_(namespace_);
node.add_workload_name(workload_name);
node.add_platform_metadata(platform_metadata_offset);
auto data = node.Finish();
fbb.Finish(data);
return *flatbuffers::GetRoot<::Wasm::Common::FlatNode>(fbb.GetBufferPointer());
}
::Wasm::Common::RequestInfo requestInfo(int response_code = 200) {
::Wasm::Common::RequestInfo request_info;
request_info.start_time = 0;
request_info.response_code = response_code;
request_info.request_operation = "GET";
request_info.destination_service_host = "httpbin.org";
request_info.destination_service_name = "httpbin";
request_info.response_flag = "-";
request_info.request_protocol = ::Wasm::Common::Protocol::HTTP;
request_info.destination_principal = "destination_principal";
request_info.source_principal = "source_principal";
request_info.service_auth_policy = ::Wasm::Common::ServiceAuthenticationPolicy::MutualTLS;
request_info.duration = 10000000000; // 10s in nanoseconds
request_info.url_scheme = "http";
request_info.url_host = "httpbin.org";
request_info.url_path = "/headers";
request_info.path = "/headers?retry=true";
request_info.request_id = "123";
request_info.b3_trace_id = "123abc";
request_info.b3_span_id = "abc123";
request_info.b3_trace_sampled = true;
request_info.user_agent = "chrome";
request_info.referer = "www.google.com";
request_info.source_address = "1.1.1.1";
request_info.destination_address = "2.2.2.2";
request_info.connection_id = 0;
request_info.route_name = "redirect";
request_info.upstream_cluster = "server-inbound-cluster";
request_info.upstream_host = "1.1.1.1:1000";
request_info.requested_server_name = "server.com";
request_info.x_envoy_original_dst_host = "tmp.com";
request_info.x_envoy_original_path = "/tmp";
return request_info;
}
std::string write_audit_request_json = R"({
"logName":"projects/test_project/logs/server-istio-audit-log",
"resource":{
"type":"k8s_container",
"labels":{
"cluster_name":"test_cluster",
"pod_name":"test_pod",
"location":"test_location",
"namespace_name":"test_namespace",
"project_id":"test_project",
"container_name":"istio-proxy"
}
},
"labels":{
"destination_workload":"test_workload",
"destination_namespace":"test_namespace"
},
"entries":[
{
"httpRequest":{
"requestMethod":"GET",
"requestUrl":"http://httpbin.org/headers?retry=true",
"userAgent":"chrome",
"remoteIp":"1.1.1.1",
"referer":"www.google.com",
"serverIp":"2.2.2.2",
"latency":"10s",
"protocol":"http",
"status":"200"
},
"timestamp":"1970-01-01T00:00:00Z",
"severity":"INFO",
"labels":{
"destination_principal":"destination_principal",
"destination_service_host":"httpbin.org",
"destination_service_name":"httpbin",
"request_id":"123",
"source_namespace":"test_peer_namespace",
"source_principal":"source_principal",
"source_workload":"test_peer_workload",
},
"trace":"projects/test_project/traces/123abc",
"spanId":"abc123",
"traceSampled":true
}
]
})";
std::string write_log_request_json = R"({
"logName":"projects/test_project/logs/server-accesslog-stackdriver",
"resource":{
"type":"k8s_container",
"labels":{
"cluster_name":"test_cluster",
"pod_name":"test_pod",
"location":"test_location",
"namespace_name":"test_namespace",
"project_id":"test_project",
"container_name":"istio-proxy"
}
},
"labels":{
"destination_workload":"test_workload",
"mesh_uid":"mesh",
"destination_namespace":"test_namespace",
"destination_name":"test_pod"
},
"entries":[
{
"httpRequest":{
"requestMethod":"GET",
"requestUrl":"http://httpbin.org/headers?retry=true",
"userAgent":"chrome",
"remoteIp":"1.1.1.1",
"referer":"www.google.com",
"serverIp":"2.2.2.2",
"latency":"10s",
"protocol":"http",
"status":"200"
},
"timestamp":"1970-01-01T00:00:00Z",
"severity":"INFO",
"labels":{
"source_name":"test_peer_pod",
"destination_principal":"destination_principal",
"destination_service_host":"httpbin.org",
"destination_service_name":"httpbin",
"request_id":"123",
"source_namespace":"test_peer_namespace",
"source_principal":"source_principal",
"service_authentication_policy":"MUTUAL_TLS",
"source_workload":"test_peer_workload",
"response_flag":"-",
"protocol":"http",
"log_sampled":"false",
"connection_id":"0",
"upstream_cluster": "server-inbound-cluster",
"route_name": "redirect",
"requested_server_name": "server.com",
"x-envoy-original-dst-host": "tmp.com",
"x-envoy-original-path": "/tmp",
"upstream_host": "1.1.1.1:1000"
},
"trace":"projects/test_project/traces/123abc",
"spanId":"abc123",
"traceSampled":true
}
]
})";
std::string write_error_log_request_json = R"({
"logName":"projects/test_project/logs/server-accesslog-stackdriver",
"resource":{
"type":"k8s_container",
"labels":{
"cluster_name":"test_cluster",
"pod_name":"test_pod",
"location":"test_location",
"namespace_name":"test_namespace",
"project_id":"test_project",
"container_name":"istio-proxy"
}
},
"labels":{
"destination_workload":"test_workload",
"mesh_uid":"mesh",
"destination_namespace":"test_namespace",
"destination_name":"test_pod"
},
"entries":[
{
"httpRequest":{
"requestMethod":"GET",
"requestUrl":"http://httpbin.org/headers?retry=true",
"userAgent":"chrome",
"remoteIp":"1.1.1.1",
"referer":"www.google.com",
"serverIp":"2.2.2.2",
"latency":"10s",
"protocol":"http",
"status":"404",
},
"timestamp":"1970-01-01T00:00:00Z",
"severity":"ERROR",
"labels":{
"source_name":"test_peer_pod",
"destination_principal":"destination_principal",
"destination_service_host":"httpbin.org",
"destination_service_name":"httpbin",
"request_id":"123",
"source_namespace":"test_peer_namespace",
"source_principal":"source_principal",
"service_authentication_policy":"MUTUAL_TLS",
"source_workload":"test_peer_workload",
"response_flag":"-",
"protocol":"http",
"log_sampled":"false",
"connection_id":"0",
"upstream_cluster": "server-inbound-cluster",
"route_name": "redirect",
"requested_server_name": "server.com",
"x-envoy-original-dst-host": "tmp.com",
"x-envoy-original-path": "/tmp",
"upstream_host": "1.1.1.1:1000"
},
"trace":"projects/test_project/traces/123abc",
"spanId":"abc123",
"traceSampled":true
}
]
})";
google::logging::v2::WriteLogEntriesRequest
expectedRequest(int log_entry_count, bool for_audit = false, bool use_error_log = false) {
google::logging::v2::WriteLogEntriesRequest req;
google::protobuf::util::JsonParseOptions options;
std::string non_audit_log = use_error_log ? write_error_log_request_json : write_log_request_json;
const auto status =
JsonStringToMessage((for_audit ? write_audit_request_json : non_audit_log), &req, options);
EXPECT_OK(status);
for (int i = 1; i < log_entry_count; i++) {
auto* new_entry = req.mutable_entries()->Add();
new_entry->CopyFrom(req.entries()[0]);
}
return req;
}
} // namespace
TEST(LoggerTest, TestWriteLogEntry) {
auto exporter = std::make_unique<::testing::NiceMock<MockExporter>>();
auto exporter_ptr = exporter.get();
flatbuffers::FlatBufferBuilder local, peer;
std::unordered_map<std::string, std::string> extra_labels;
auto logger = std::make_unique<Logger>(nodeInfo(local), std::move(exporter), extra_labels);
logger->addLogEntry(requestInfo(), peerNodeInfo(peer), extra_labels, false, false);
EXPECT_CALL(*exporter_ptr, exportLogs(::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(
[](const std::vector<std::unique_ptr<const google::logging::v2::WriteLogEntriesRequest>>&
requests,
bool) {
for (const auto& req : requests) {
std::string diff;
MessageDifferencer differ;
differ.ReportDifferencesToString(&diff);
if (!differ.Compare(expectedRequest(1), *req)) {
FAIL() << "unexpected log entry " << diff << "\n";
}
}
}));
logger->exportLogEntry(/* is_on_done = */ false);
}
TEST(LoggerTest, TestWriteErrorLogEntry) {
auto exporter = std::make_unique<::testing::NiceMock<MockExporter>>();
auto exporter_ptr = exporter.get();
flatbuffers::FlatBufferBuilder local, peer;
std::unordered_map<std::string, std::string> extra_labels;
auto logger = std::make_unique<Logger>(nodeInfo(local), std::move(exporter), extra_labels);
logger->addLogEntry(requestInfo(404), peerNodeInfo(peer), extra_labels, false, false);
EXPECT_CALL(*exporter_ptr, exportLogs(::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(
[](const std::vector<std::unique_ptr<const google::logging::v2::WriteLogEntriesRequest>>&
requests,
bool) {
for (const auto& req : requests) {
std::string diff;
MessageDifferencer differ;
differ.ReportDifferencesToString(&diff);
if (!differ.Compare(expectedRequest(1, false /* audit log */, true /* error log */),
*req)) {
FAIL() << "unexpected log entry " << diff << "\n";
}
}
}));
logger->exportLogEntry(/* is_on_done = */ false);
}
TEST(LoggerTest, TestWriteLogEntryRotation) {
auto exporter = std::make_unique<::testing::NiceMock<MockExporter>>();
auto exporter_ptr = exporter.get();
flatbuffers::FlatBufferBuilder local, peer;
std::unordered_map<std::string, std::string> extra_labels;
auto logger = std::make_unique<Logger>(nodeInfo(local), std::move(exporter), extra_labels, 1200);
for (int i = 0; i < 10; i++) {
logger->addLogEntry(requestInfo(), peerNodeInfo(peer), extra_labels, false, false);
}
EXPECT_CALL(*exporter_ptr, exportLogs(::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(
[](const std::vector<std::unique_ptr<const google::logging::v2::WriteLogEntriesRequest>>&
requests,
bool) {
EXPECT_EQ(requests.size(), 5);
for (const auto& req : requests) {
std::string diff;
MessageDifferencer differ;
differ.ReportDifferencesToString(&diff);
if (!differ.Compare(expectedRequest(2), *req)) {
FAIL() << "unexpected log entry " << diff << "\n";
}
}
}));
logger->exportLogEntry(/* is_on_done = */ false);
}
TEST(LoggerTest, TestWriteAuditEntry) {
auto exporter = std::make_unique<::testing::NiceMock<MockExporter>>();
auto exporter_ptr = exporter.get();
flatbuffers::FlatBufferBuilder local, peer;
std::unordered_map<std::string, std::string> extra_labels;
auto logger = std::make_unique<Logger>(nodeInfo(local), std::move(exporter), extra_labels);
logger->addLogEntry(requestInfo(), peerNodeInfo(peer), extra_labels, false, true);
EXPECT_CALL(*exporter_ptr, exportLogs(::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(
[](const std::vector<std::unique_ptr<const google::logging::v2::WriteLogEntriesRequest>>&
requests,
bool) {
for (const auto& req : requests) {
std::string diff;
MessageDifferencer differ;
differ.ReportDifferencesToString(&diff);
if (!differ.Compare(expectedRequest(1, true), *req)) {
FAIL() << "unexpected audit entry " << diff << "\n";
}
}
}));
logger->exportLogEntry(/* is_on_done = */ false);
}
TEST(LoggerTest, TestWriteAuditAndLogEntry) {
auto exporter = std::make_unique<::testing::NiceMock<MockExporter>>();
auto exporter_ptr = exporter.get();
flatbuffers::FlatBufferBuilder local, peer;
std::unordered_map<std::string, std::string> extra_labels;
auto logger = std::make_unique<Logger>(nodeInfo(local), std::move(exporter), extra_labels);
for (int i = 0; i < 5; i++) {
logger->addLogEntry(requestInfo(), peerNodeInfo(peer), extra_labels, false, false);
logger->addLogEntry(requestInfo(), peerNodeInfo(peer), extra_labels, false, true);
}
EXPECT_CALL(*exporter_ptr, exportLogs(::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(
[](const std::vector<std::unique_ptr<const google::logging::v2::WriteLogEntriesRequest>>&
requests,
bool) {
bool foundAudit = false;
bool foundLog = false;
std::string diff;
EXPECT_EQ(requests.size(), 2);
for (const auto& req : requests) {
MessageDifferencer differ;
differ.ReportDifferencesToString(&diff);
if (differ.Compare(expectedRequest(5, true), *req)) {
foundAudit = true;
}
if (differ.Compare(expectedRequest(5, false), *req)) {
foundLog = true;
}
}
if (!(foundAudit && foundLog)) {
FAIL() << "unexpected entries, last difference: " << diff << "\n";
}
}));
logger->exportLogEntry(/* is_on_done = */ false);
}
} // namespace Log
} // namespace Stackdriver
} // namespace Extensions

View File

@ -0,0 +1,64 @@
# 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.
#
################################################################################
#
licenses(["notice"])
load(
"@envoy//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_cc_test",
)
load(
"@envoy//test/extensions:extensions_build_system.bzl",
"envoy_extension_cc_test",
)
envoy_cc_library(
name = "metric",
srcs = [
"record.cc",
"registry.cc",
],
hdrs = [
"record.h",
"registry.h",
],
repository = "@envoy",
visibility = [
"//extensions/stackdriver:__pkg__",
],
deps = [
"//extensions/common:context",
"//extensions/stackdriver/common:constants",
"//extensions/stackdriver/common:utils",
"//extensions/stackdriver/config/v1alpha1:stackdriver_plugin_config_cc_proto",
"@io_opencensus_cpp//opencensus/exporters/stats/stackdriver:stackdriver_exporter",
"@io_opencensus_cpp//opencensus/stats",
],
)
envoy_extension_cc_test(
name = "registry_test",
size = "small",
srcs = ["registry_test.cc"],
extension_names = ["envoy.filters.http.wasm"],
repository = "@envoy",
deps = [
":metric",
"@envoy//test/test_common:wasm_lib",
],
)

View File

@ -0,0 +1,478 @@
/* 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 "extensions/stackdriver/metric/record.h"
#include "extensions/stackdriver/common/constants.h"
#include "extensions/stackdriver/metric/registry.h"
#include "google/protobuf/util/time_util.h"
using google::protobuf::util::TimeUtil;
namespace Extensions {
namespace Stackdriver {
namespace Metric {
namespace {
using Common::unknownIfEmpty;
std::string getLocalCanonicalName(const ::Wasm::Common::FlatNode& local_node_info) {
const auto local_labels = local_node_info.labels();
const auto local_name_iter =
local_labels ? local_labels->LookupByKey(Wasm::Common::kCanonicalServiceLabelName.data())
: nullptr;
const auto local_canonical_name =
local_name_iter ? local_name_iter->value() : local_node_info.workload_name();
return flatbuffers::GetString(local_canonical_name);
}
std::string getLocalCanonicalRev(const ::Wasm::Common::FlatNode& local_node_info) {
const auto local_labels = local_node_info.labels();
const auto local_rev_iter =
local_labels
? local_labels->LookupByKey(Wasm::Common::kCanonicalServiceRevisionLabelName.data())
: nullptr;
const auto local_canonical_rev = local_rev_iter ? local_rev_iter->value() : nullptr;
return local_canonical_rev ? local_canonical_rev->str() : ::Wasm::Common::kLatest.data();
}
std::string getPeerCanonicalName(const ::Wasm::Common::FlatNode& peer_node_info) {
const auto peer_labels = peer_node_info.labels();
const auto peer_name_iter =
peer_labels ? peer_labels->LookupByKey(Wasm::Common::kCanonicalServiceLabelName.data())
: nullptr;
const auto peer_canonical_name =
peer_name_iter ? peer_name_iter->value() : peer_node_info.workload_name();
return flatbuffers::GetString(peer_canonical_name);
}
std::string getPeerCanonicalRev(const ::Wasm::Common::FlatNode& peer_node_info) {
const auto peer_labels = peer_node_info.labels();
const auto peer_rev_iter =
peer_labels
? peer_labels->LookupByKey(Wasm::Common::kCanonicalServiceRevisionLabelName.data())
: nullptr;
const auto peer_canonical_rev = peer_rev_iter ? peer_rev_iter->value() : nullptr;
return peer_canonical_rev ? peer_canonical_rev->str() : ::Wasm::Common::kLatest.data();
}
TagKeyValueList getOutboundTagMap(const ::Wasm::Common::FlatNode& local_node_info,
const ::Wasm::Common::FlatNode& peer_node_info,
const ::Wasm::Common::RequestInfo& request_info) {
TagKeyValueList outboundMap = {
{meshUIDKey(), unknownIfEmpty(flatbuffers::GetString(local_node_info.mesh_id()))},
{requestProtocolKey(),
unknownIfEmpty(std::string(::Wasm::Common::ProtocolString(request_info.request_protocol)))},
{serviceAuthenticationPolicyKey(),
unknownIfEmpty(std::string(
::Wasm::Common::AuthenticationPolicyString(request_info.service_auth_policy)))},
{destinationServiceNameKey(), unknownIfEmpty(request_info.destination_service_name)},
{destinationServiceNamespaceKey(),
unknownIfEmpty(flatbuffers::GetString(peer_node_info.namespace_()))},
{destinationPortKey(), unknownIfEmpty(std::to_string(request_info.destination_port))},
{sourcePrincipalKey(), unknownIfEmpty(request_info.source_principal)},
{sourceWorkloadNameKey(),
unknownIfEmpty(flatbuffers::GetString(local_node_info.workload_name()))},
{sourceWorkloadNamespaceKey(),
unknownIfEmpty(flatbuffers::GetString(local_node_info.namespace_()))},
{sourceOwnerKey(), unknownIfEmpty(Common::getOwner(local_node_info))},
{destinationPrincipalKey(), unknownIfEmpty(request_info.destination_principal)},
{destinationWorkloadNameKey(),
unknownIfEmpty(flatbuffers::GetString(peer_node_info.workload_name()))},
{destinationWorkloadNamespaceKey(),
unknownIfEmpty(flatbuffers::GetString(peer_node_info.namespace_()))},
{destinationOwnerKey(), unknownIfEmpty(Common::getOwner(peer_node_info))},
{destinationCanonicalServiceNameKey(), unknownIfEmpty(getPeerCanonicalName(peer_node_info))},
{destinationCanonicalServiceNamespaceKey(),
unknownIfEmpty(flatbuffers::GetString(peer_node_info.namespace_()))},
{destinationCanonicalRevisionKey(), unknownIfEmpty(getPeerCanonicalRev(peer_node_info))},
{sourceCanonicalServiceNameKey(), unknownIfEmpty(getLocalCanonicalName(local_node_info))},
{sourceCanonicalServiceNamespaceKey(),
unknownIfEmpty(flatbuffers::GetString(local_node_info.namespace_()))},
{sourceCanonicalRevisionKey(), unknownIfEmpty(getLocalCanonicalRev(local_node_info))},
{proxyVersionKey(), unknownIfEmpty(flatbuffers::GetString(local_node_info.istio_version()))}};
return outboundMap;
}
TagKeyValueList getInboundTagMap(const ::Wasm::Common::FlatNode& local_node_info,
const ::Wasm::Common::FlatNode& peer_node_info,
const ::Wasm::Common::RequestInfo& request_info) {
TagKeyValueList inboundMap = {
{meshUIDKey(), unknownIfEmpty(flatbuffers::GetString(local_node_info.mesh_id()))},
{requestProtocolKey(),
unknownIfEmpty(std::string(::Wasm::Common::ProtocolString(request_info.request_protocol)))},
{serviceAuthenticationPolicyKey(),
unknownIfEmpty(std::string(
::Wasm::Common::AuthenticationPolicyString(request_info.service_auth_policy)))},
{destinationServiceNameKey(), unknownIfEmpty(request_info.destination_service_name)},
{destinationServiceNamespaceKey(),
unknownIfEmpty(flatbuffers::GetString(local_node_info.namespace_()))},
{destinationPortKey(), unknownIfEmpty(std::to_string(request_info.destination_port))},
{sourcePrincipalKey(), unknownIfEmpty(request_info.source_principal)},
{sourceWorkloadNameKey(),
unknownIfEmpty(flatbuffers::GetString(peer_node_info.workload_name()))},
{sourceWorkloadNamespaceKey(),
unknownIfEmpty(flatbuffers::GetString(peer_node_info.namespace_()))},
{sourceOwnerKey(), unknownIfEmpty(Common::getOwner(peer_node_info))},
{destinationPrincipalKey(), unknownIfEmpty(request_info.destination_principal)},
{destinationWorkloadNameKey(),
unknownIfEmpty(flatbuffers::GetString(local_node_info.workload_name()))},
{destinationWorkloadNamespaceKey(),
unknownIfEmpty(flatbuffers::GetString(local_node_info.namespace_()))},
{destinationOwnerKey(), unknownIfEmpty(Common::getOwner(local_node_info))},
{destinationCanonicalServiceNameKey(),
unknownIfEmpty(getLocalCanonicalName(local_node_info))},
{destinationCanonicalServiceNamespaceKey(),
unknownIfEmpty(flatbuffers::GetString(local_node_info.namespace_()))},
{destinationCanonicalRevisionKey(), unknownIfEmpty(getLocalCanonicalRev(local_node_info))},
{sourceCanonicalServiceNameKey(), unknownIfEmpty(getPeerCanonicalName(peer_node_info))},
{sourceCanonicalServiceNamespaceKey(),
unknownIfEmpty(flatbuffers::GetString(peer_node_info.namespace_()))},
{sourceCanonicalRevisionKey(), unknownIfEmpty(getPeerCanonicalRev(peer_node_info))},
{proxyVersionKey(), unknownIfEmpty(flatbuffers::GetString(local_node_info.istio_version()))}};
return inboundMap;
}
// See:
// https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
uint32_t httpCodeFromGrpc(uint32_t grpc_status) {
switch (grpc_status) {
case 0: // OK
return 200;
case 1: // CANCELLED
return 499;
case 2: // UNKNOWN
return 500;
case 3: // INVALID_ARGUMENT
return 400;
case 4: // DEADLINE_EXCEEDED
return 504;
case 5: // NOT_FOUND
return 404;
case 6: // ALREADY_EXISTS
return 409;
case 7: // PERMISSION_DENIED
return 403;
case 8: // RESOURCE_EXHAUSTED
return 429;
case 9: // FAILED_PRECONDITION
return 400;
case 10: // ABORTED
return 409;
case 11: // OUT_OF_RANGE
return 400;
case 12: // UNIMPLEMENTED
return 501;
case 13: // INTERNAL
return 500;
case 14: // UNAVAILABLE
return 503;
case 15: // DATA_LOSS
return 500;
case 16: // UNAUTHENTICATED
return 401;
default:
return 500;
}
}
void addHttpSpecificTags(const ::Wasm::Common::RequestInfo& request_info,
TagKeyValueList& tag_map) {
const auto& operation = request_info.request_protocol == ::Wasm::Common::Protocol::GRPC
? request_info.url_path
: request_info.request_operation;
tag_map.emplace_back(Metric::requestOperationKey(), operation);
const auto& response_code = request_info.request_protocol == ::Wasm::Common::Protocol::GRPC
? httpCodeFromGrpc(request_info.grpc_status)
: request_info.response_code;
tag_map.emplace_back(Metric::responseCodeKey(), std::to_string(response_code));
}
TagKeyValueList getMetricTagMap(const TagKeyValueList& input_map,
const TagKeyValueList& tag_overrides) {
if (tag_overrides.empty()) {
return input_map;
}
TagKeyValueList out;
for (const auto& [tag_key, value_list] : input_map) {
const auto& name = tag_key.name();
auto it = std::find_if(tag_overrides.begin(), tag_overrides.end(),
[&name](const auto& override) { return override.first.name() == name; });
if (it != tag_overrides.end()) {
out.emplace_back(tag_key, it->second);
} else {
out.emplace_back(tag_key, value_list);
}
}
auto it = std::find_if(tag_overrides.begin(), tag_overrides.end(), [](const auto& override) {
return override.first.name() == "api_version";
});
if (it != tag_overrides.end()) {
out.emplace_back(apiVersionKey(), it->second);
}
it = std::find_if(tag_overrides.begin(), tag_overrides.end(),
[](const auto& override) { return override.first.name() == "api_name"; });
if (it != tag_overrides.end()) {
out.emplace_back(apiNameKey(), it->second);
}
return out;
}
bool hasOverridesMatching(const override_map& overrides, const std::string& metric) {
if (overrides.empty()) {
return false;
}
auto it = std::find_if(
overrides.begin(), overrides.end(),
[&metric](const override_map_value_type& vt) { return absl::StrContains(vt.first, metric); });
return it != overrides.end();
}
} // namespace
void record(bool is_outbound, const ::Wasm::Common::FlatNode& local_node_info,
const ::Wasm::Common::FlatNode& peer_node_info,
const ::Wasm::Common::RequestInfo& request_info, bool record_http_size_metrics,
const override_map& overrides) {
double latency_ms = request_info.duration /* in nanoseconds */ / 1000000.0;
if (is_outbound) {
TagKeyValueList tagMap = getOutboundTagMap(local_node_info, peer_node_info, request_info);
addHttpSpecificTags(request_info, tagMap);
if (hasOverridesMatching(overrides, "client")) {
auto it = overrides.find(Common::kClientRequestCountView);
if (it == overrides.end()) {
opencensus::stats::Record({{clientRequestCountMeasure(), 1}}, tagMap);
} else {
opencensus::stats::Record({{clientRequestCountMeasure(), 1}},
getMetricTagMap(tagMap, it->second));
}
it = overrides.find(Common::kClientRoundtripLatenciesView);
if (it == overrides.end()) {
opencensus::stats::Record({{clientRoundtripLatenciesMeasure(), latency_ms}}, tagMap);
} else {
opencensus::stats::Record({{clientRoundtripLatenciesMeasure(), latency_ms}},
getMetricTagMap(tagMap, it->second));
}
it = overrides.find(Common::kClientRequestBytesView);
if (it == overrides.end()) {
opencensus::stats::Record({{clientRequestBytesMeasure(), request_info.request_size}},
tagMap);
} else {
opencensus::stats::Record({{clientRequestBytesMeasure(), request_info.request_size}},
getMetricTagMap(tagMap, it->second));
}
it = overrides.find(Common::kClientResponseBytesView);
if (it == overrides.end()) {
opencensus::stats::Record({{clientResponseBytesMeasure(), request_info.response_size}},
tagMap);
} else {
opencensus::stats::Record({{clientResponseBytesMeasure(), request_info.response_size}},
getMetricTagMap(tagMap, it->second));
}
return;
}
if (record_http_size_metrics) {
opencensus::stats::Record({{clientRequestCountMeasure(), 1},
{clientRoundtripLatenciesMeasure(), latency_ms},
{clientRequestBytesMeasure(), request_info.request_size},
{clientResponseBytesMeasure(), request_info.response_size}},
tagMap);
} else {
opencensus::stats::Record(
{{clientRequestCountMeasure(), 1}, {clientRoundtripLatenciesMeasure(), latency_ms}},
tagMap);
}
return;
}
TagKeyValueList tagMap = getInboundTagMap(local_node_info, peer_node_info, request_info);
addHttpSpecificTags(request_info, tagMap);
if (hasOverridesMatching(overrides, "server")) {
auto it = overrides.find(Common::kServerRequestCountView);
if (it == overrides.end()) {
opencensus::stats::Record({{serverRequestCountMeasure(), 1}}, tagMap);
} else {
opencensus::stats::Record({{serverRequestCountMeasure(), 1}},
getMetricTagMap(tagMap, it->second));
}
it = overrides.find(Common::kServerResponseLatenciesView);
if (it == overrides.end()) {
opencensus::stats::Record({{serverResponseLatenciesMeasure(), latency_ms}}, tagMap);
} else {
opencensus::stats::Record({{serverResponseLatenciesMeasure(), latency_ms}},
getMetricTagMap(tagMap, it->second));
}
it = overrides.find(Common::kServerRequestBytesView);
if (it == overrides.end()) {
opencensus::stats::Record({{serverRequestBytesMeasure(), request_info.request_size}}, tagMap);
} else {
opencensus::stats::Record({{serverRequestBytesMeasure(), request_info.request_size}},
getMetricTagMap(tagMap, it->second));
}
it = overrides.find(Common::kServerResponseBytesView);
if (it == overrides.end()) {
opencensus::stats::Record({{serverResponseBytesMeasure(), request_info.response_size}},
tagMap);
} else {
opencensus::stats::Record({{serverResponseBytesMeasure(), request_info.response_size}},
getMetricTagMap(tagMap, it->second));
}
return;
}
if (record_http_size_metrics) {
opencensus::stats::Record({{serverRequestCountMeasure(), 1},
{serverResponseLatenciesMeasure(), latency_ms},
{serverRequestBytesMeasure(), request_info.request_size},
{serverResponseBytesMeasure(), request_info.response_size}},
tagMap);
} else {
opencensus::stats::Record(
{{serverRequestCountMeasure(), 1}, {serverResponseLatenciesMeasure(), latency_ms}}, tagMap);
}
}
void recordTCP(bool is_outbound, const ::Wasm::Common::FlatNode& local_node_info,
const ::Wasm::Common::FlatNode& peer_node_info,
const ::Wasm::Common::RequestInfo& request_info, const override_map& overrides) {
if (is_outbound) {
TagKeyValueList tagMap = getOutboundTagMap(local_node_info, peer_node_info, request_info);
if (hasOverridesMatching(overrides, "client")) {
auto it = overrides.find(Common::kClientConnectionsOpenCountView);
if (it == overrides.end()) {
opencensus::stats::Record(
{{clientConnectionsOpenCountMeasure(), request_info.tcp_connections_opened}}, tagMap);
} else {
opencensus::stats::Record(
{{clientConnectionsOpenCountMeasure(), request_info.tcp_connections_opened}},
getMetricTagMap(tagMap, it->second));
}
it = overrides.find(Common::kClientConnectionsCloseCountView);
if (it == overrides.end()) {
opencensus::stats::Record(
{{clientConnectionsCloseCountMeasure(), request_info.tcp_connections_closed}}, tagMap);
} else {
opencensus::stats::Record(
{{clientConnectionsCloseCountMeasure(), request_info.tcp_connections_closed}},
getMetricTagMap(tagMap, it->second));
}
it = overrides.find(Common::kClientReceivedBytesCountView);
if (it == overrides.end()) {
opencensus::stats::Record(
{{clientReceivedBytesCountMeasure(), request_info.tcp_received_bytes}}, tagMap);
} else {
opencensus::stats::Record(
{{clientReceivedBytesCountMeasure(), request_info.tcp_received_bytes}},
getMetricTagMap(tagMap, it->second));
}
it = overrides.find(Common::kClientSentBytesCountView);
if (it == overrides.end()) {
opencensus::stats::Record({{clientSentBytesCountMeasure(), request_info.tcp_sent_bytes}},
tagMap);
} else {
opencensus::stats::Record({{clientSentBytesCountMeasure(), request_info.tcp_sent_bytes}},
getMetricTagMap(tagMap, it->second));
}
return;
}
opencensus::stats::Record(
{{clientConnectionsOpenCountMeasure(), request_info.tcp_connections_opened},
{clientConnectionsCloseCountMeasure(), request_info.tcp_connections_closed},
{clientReceivedBytesCountMeasure(), request_info.tcp_received_bytes},
{clientSentBytesCountMeasure(), request_info.tcp_sent_bytes}},
tagMap);
return;
}
TagKeyValueList tagMap = getInboundTagMap(local_node_info, peer_node_info, request_info);
if (hasOverridesMatching(overrides, "server")) {
auto it = overrides.find(Common::kServerConnectionsOpenCountView);
if (it == overrides.end()) {
opencensus::stats::Record(
{{serverConnectionsOpenCountMeasure(), request_info.tcp_connections_opened}}, tagMap);
} else {
opencensus::stats::Record(
{{serverConnectionsOpenCountMeasure(), request_info.tcp_connections_opened}},
getMetricTagMap(tagMap, it->second));
}
it = overrides.find(Common::kServerConnectionsCloseCountView);
if (it == overrides.end()) {
opencensus::stats::Record(
{{serverConnectionsCloseCountMeasure(), request_info.tcp_connections_closed}}, tagMap);
} else {
opencensus::stats::Record(
{{serverConnectionsCloseCountMeasure(), request_info.tcp_connections_closed}},
getMetricTagMap(tagMap, it->second));
}
it = overrides.find(Common::kServerReceivedBytesCountView);
if (it == overrides.end()) {
opencensus::stats::Record(
{{serverReceivedBytesCountMeasure(), request_info.tcp_received_bytes}}, tagMap);
} else {
opencensus::stats::Record(
{{serverReceivedBytesCountMeasure(), request_info.tcp_received_bytes}},
getMetricTagMap(tagMap, it->second));
}
it = overrides.find(Common::kServerSentBytesCountView);
if (it == overrides.end()) {
opencensus::stats::Record({{serverSentBytesCountMeasure(), request_info.tcp_sent_bytes}},
tagMap);
} else {
opencensus::stats::Record({{serverSentBytesCountMeasure(), request_info.tcp_sent_bytes}},
getMetricTagMap(tagMap, it->second));
}
return;
}
opencensus::stats::Record(
{{serverConnectionsOpenCountMeasure(), request_info.tcp_connections_opened},
{serverConnectionsCloseCountMeasure(), request_info.tcp_connections_closed},
{serverReceivedBytesCountMeasure(), request_info.tcp_received_bytes},
{serverSentBytesCountMeasure(), request_info.tcp_sent_bytes}},
tagMap);
return;
}
} // namespace Metric
} // namespace Stackdriver
} // namespace Extensions

View File

@ -0,0 +1,45 @@
/* 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 "extensions/common/context.h"
#include "extensions/stackdriver/config/v1alpha1/stackdriver_plugin_config.pb.h"
#include "opencensus/stats/tag_key.h"
namespace Extensions {
namespace Stackdriver {
namespace Metric {
typedef std::vector<std::pair<opencensus::tags::TagKey, std::string>> TagKeyValueList;
typedef std::unordered_map<std::string, TagKeyValueList> override_map;
typedef std::unordered_map<std::string, TagKeyValueList>::value_type override_map_value_type;
// Record metrics based on local node info and request info.
// Reporter kind deceides the type of metrics to record.
void record(bool is_outbound, const ::Wasm::Common::FlatNode& local_node_info,
const ::Wasm::Common::FlatNode& peer_node_info,
const ::Wasm::Common::RequestInfo& request_info, bool record_http_size_metrics,
const override_map& overrides);
// Record TCP metrics based on local node info and request info.
// Reporter kind deceides the type of metrics to record.
void recordTCP(bool is_outbound, const ::Wasm::Common::FlatNode& local_node_info,
const ::Wasm::Common::FlatNode& peer_node_info,
const ::Wasm::Common::RequestInfo& request_info, const override_map& overrides);
} // namespace Metric
} // namespace Stackdriver
} // namespace Extensions

View File

@ -0,0 +1,435 @@
/* 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 "extensions/stackdriver/metric/registry.h"
#include <fstream>
#include <sstream>
#include "extensions/stackdriver/common/constants.h"
#include "google/api/monitored_resource.pb.h"
#include "grpcpp/grpcpp.h"
#include "grpcpp/security/tls_certificate_provider.h"
namespace Extensions {
namespace Stackdriver {
namespace Metric {
namespace {
class GoogleUserProjHeaderInterceptor : public grpc::experimental::Interceptor {
public:
GoogleUserProjHeaderInterceptor(const std::string& project_id) : project_id_(project_id) {}
virtual void Intercept(grpc::experimental::InterceptorBatchMethods* methods) {
if (methods->QueryInterceptionHookPoint(
grpc::experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) {
auto* metadata_map = methods->GetSendInitialMetadata();
if (metadata_map != nullptr) {
metadata_map->insert(std::make_pair("x-goog-user-project", project_id_));
}
}
methods->Proceed();
}
private:
const std::string& project_id_;
};
class GoogleUserProjHeaderInterceptorFactory
: public grpc::experimental::ClientInterceptorFactoryInterface {
public:
GoogleUserProjHeaderInterceptorFactory(const std::string& project_id) : project_id_(project_id) {}
virtual grpc::experimental::Interceptor*
CreateClientInterceptor(grpc::experimental::ClientRpcInfo*) override {
return new GoogleUserProjHeaderInterceptor(project_id_);
}
private:
std::string project_id_;
};
} // namespace
using namespace Extensions::Stackdriver::Common;
using namespace opencensus::exporters::stats;
using namespace opencensus::stats;
// Gets opencensus stackdriver exporter options.
StackdriverOptions
getStackdriverOptions(const Wasm::Common::FlatNode& local_node_info,
const ::Extensions::Stackdriver::Common::StackdriverStubOption& stub_option) {
StackdriverOptions options;
auto platform_metadata = local_node_info.platform_metadata();
if (platform_metadata) {
auto project = platform_metadata->LookupByKey(kGCPProjectKey);
if (project) {
options.project_id = flatbuffers::GetString(project->value());
}
}
grpc::experimental::TlsChannelCredentialsOptions tls_options;
tls_options.set_max_tls_version(grpc_tls_version::TLS1_2);
if (!stub_option.test_root_pem_path.empty()) {
std::ifstream file(stub_option.test_root_pem_path);
if (!file.fail()) {
std::stringstream file_string;
file_string << file.rdbuf();
tls_options.set_certificate_provider(
std::make_shared<grpc::experimental::StaticDataCertificateProvider>(file_string.str()));
tls_options.watch_root_certs();
}
}
auto channel_creds = grpc::experimental::TlsCredentials(tls_options);
if (!stub_option.insecure_endpoint.empty()) {
auto channel =
grpc::CreateChannel(stub_option.insecure_endpoint, grpc::InsecureChannelCredentials());
options.metric_service_stub = google::monitoring::v3::MetricService::NewStub(channel);
} else if (!stub_option.sts_port.empty()) {
::grpc::experimental::StsCredentialsOptions sts_options;
std::string token_path =
stub_option.test_token_path.empty() ? kSTSSubjectTokenPath : stub_option.test_token_path;
::Extensions::Stackdriver::Common::setSTSCallCredentialOptions(
&sts_options, stub_option.sts_port, token_path);
auto call_creds = grpc::experimental::StsCredentials(sts_options);
grpc::ChannelArguments args;
std::vector<std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>> creators;
auto header_factory =
std::make_unique<GoogleUserProjHeaderInterceptorFactory>(options.project_id);
creators.push_back(std::move(header_factory));
// When STS is turned on, first check if secure_endpoint is set or not,
// which indicates whether this is for testing senario. If not set, check
// for monitoring_endpoint override, which indicates a different SD backend
// endpoint, such as staging.
std::string monitoring_endpoint = stub_option.default_endpoint;
if (!stub_option.secure_endpoint.empty()) {
monitoring_endpoint = stub_option.secure_endpoint;
} else if (!stub_option.monitoring_endpoint.empty()) {
monitoring_endpoint = stub_option.monitoring_endpoint;
}
auto channel = ::grpc::experimental::CreateCustomChannelWithInterceptors(
monitoring_endpoint, grpc::CompositeChannelCredentials(channel_creds, call_creds), args,
std::move(creators));
options.metric_service_stub = google::monitoring::v3::MetricService::NewStub(channel);
} else if (!stub_option.secure_endpoint.empty()) {
auto channel = grpc::CreateChannel(stub_option.secure_endpoint, channel_creds);
options.metric_service_stub = google::monitoring::v3::MetricService::NewStub(channel);
} else if (!stub_option.monitoring_endpoint.empty()) {
auto channel =
::grpc::CreateChannel(stub_option.monitoring_endpoint, ::grpc::GoogleDefaultCredentials());
options.metric_service_stub = google::monitoring::v3::MetricService::NewStub(channel);
}
std::string server_type = kContainerMonitoredResource;
std::string client_type = kPodMonitoredResource;
if (!platform_metadata) {
server_type = kGenericNode;
client_type = kGenericNode;
} else if (!platform_metadata->LookupByKey(kGCPClusterNameKey)) {
// if there is no cluster name key, assume it is not on kubernetes
if (platform_metadata->LookupByKey(kGCPGCEInstanceIDKey) ||
platform_metadata->LookupByKey(kGCECreatedByKey.data())) {
// if there is instance ID or createdBy key, assume it is a GCE_INSTANCE
server_type = kGCEInstanceMonitoredResource;
client_type = kGCEInstanceMonitoredResource;
} else {
// absent GCE key info, use Generic Node
server_type = kGenericNode;
client_type = kGenericNode;
}
}
// Get server and client monitored resource.
google::api::MonitoredResource server_monitored_resource;
Common::getMonitoredResource(server_type, local_node_info, &server_monitored_resource);
google::api::MonitoredResource client_monitored_resource;
Common::getMonitoredResource(client_type, local_node_info, &client_monitored_resource);
options.per_metric_monitored_resource[kServerRequestCountView] = server_monitored_resource;
options.per_metric_monitored_resource[kServerRequestBytesView] = server_monitored_resource;
options.per_metric_monitored_resource[kServerResponseBytesView] = server_monitored_resource;
options.per_metric_monitored_resource[kServerResponseLatenciesView] = server_monitored_resource;
options.per_metric_monitored_resource[kServerConnectionsOpenCountView] =
server_monitored_resource;
options.per_metric_monitored_resource[kServerConnectionsCloseCountView] =
server_monitored_resource;
options.per_metric_monitored_resource[kServerReceivedBytesCountView] = server_monitored_resource;
options.per_metric_monitored_resource[kServerSentBytesCountView] = server_monitored_resource;
options.per_metric_monitored_resource[kClientRequestCountView] = client_monitored_resource;
options.per_metric_monitored_resource[kClientRequestBytesView] = client_monitored_resource;
options.per_metric_monitored_resource[kClientResponseBytesView] = client_monitored_resource;
options.per_metric_monitored_resource[kClientRoundtripLatenciesView] = client_monitored_resource;
options.per_metric_monitored_resource[kClientConnectionsOpenCountView] =
client_monitored_resource;
options.per_metric_monitored_resource[kClientConnectionsCloseCountView] =
client_monitored_resource;
options.per_metric_monitored_resource[kClientReceivedBytesCountView] = client_monitored_resource;
options.per_metric_monitored_resource[kClientSentBytesCountView] = client_monitored_resource;
options.metric_name_prefix = kIstioMetricPrefix;
return options;
}
/*
* view function macros
*/
#define REGISTER_COUNT_VIEW(_v) \
void register##_v##View(absl::Duration expiry_duration, \
std::vector<std::string> dropped_metrics) { \
auto iter = std::find(dropped_metrics.begin(), dropped_metrics.end(), k##_v##View); \
if (iter != dropped_metrics.end()) { \
return; \
} \
const ViewDescriptor view_descriptor = ViewDescriptor() \
.set_name(k##_v##View) \
.set_measure(k##_v##Measure) \
.set_expiry_duration(expiry_duration) \
.set_aggregation(Aggregation::Count()) ADD_TAGS; \
View view(view_descriptor); \
view_descriptor.RegisterForExport(); \
}
#define REGISTER_TCP_COUNT_VIEW(_v) \
void register##_v##View(absl::Duration expiry_duration, \
std::vector<std::string> dropped_metrics) { \
auto iter = std::find(dropped_metrics.begin(), dropped_metrics.end(), k##_v##View); \
if (iter != dropped_metrics.end()) { \
return; \
} \
const ViewDescriptor view_descriptor = ViewDescriptor() \
.set_name(k##_v##View) \
.set_measure(k##_v##Measure) \
.set_expiry_duration(expiry_duration) \
.set_aggregation(Aggregation::Count()) \
ADD_COMMON_TAGS; \
View view(view_descriptor); \
view_descriptor.RegisterForExport(); \
}
#define REGISTER_TCP_SUM_VIEW(_v) \
void register##_v##View(absl::Duration expiry_duration, \
std::vector<std::string> dropped_metrics) { \
auto iter = std::find(dropped_metrics.begin(), dropped_metrics.end(), k##_v##View); \
if (iter != dropped_metrics.end()) { \
return; \
} \
const ViewDescriptor view_descriptor = ViewDescriptor() \
.set_name(k##_v##View) \
.set_measure(k##_v##Measure) \
.set_expiry_duration(expiry_duration) \
.set_aggregation(Aggregation::Sum()) \
ADD_COMMON_TAGS; \
View view(view_descriptor); \
view_descriptor.RegisterForExport(); \
}
#define REGISTER_DISTRIBUTION_VIEW(_v) \
void register##_v##View(absl::Duration expiry_duration, \
std::vector<std::string> dropped_metrics) { \
auto iter = std::find(dropped_metrics.begin(), dropped_metrics.end(), k##_v##View); \
if (iter != dropped_metrics.end()) { \
return; \
} \
const ViewDescriptor view_descriptor = \
ViewDescriptor() \
.set_name(k##_v##View) \
.set_measure(k##_v##Measure) \
.set_expiry_duration(expiry_duration) \
.set_aggregation(Aggregation::Distribution(BucketBoundaries::Exponential(20, 1, 2))) \
ADD_TAGS; \
View view(view_descriptor); \
view_descriptor.RegisterForExport(); \
}
#define REGISTER_BYTES_DISTRIBUTION_VIEW(_v) \
void register##_v##View(absl::Duration expiry_duration, \
std::vector<std::string> dropped_metrics) { \
auto iter = std::find(dropped_metrics.begin(), dropped_metrics.end(), k##_v##View); \
if (iter != dropped_metrics.end()) { \
return; \
} \
const ViewDescriptor view_descriptor = \
ViewDescriptor() \
.set_name(k##_v##View) \
.set_measure(k##_v##Measure) \
.set_expiry_duration(expiry_duration) \
.set_aggregation(Aggregation::Distribution(BucketBoundaries::Exponential(7, 1, 10))) \
ADD_TAGS; \
View view(view_descriptor); \
view_descriptor.RegisterForExport(); \
}
#define ADD_TAGS ADD_COMMON_TAGS ADD_HTTP_GRPC_TAGS
#define ADD_HTTP_GRPC_TAGS \
.add_column(requestOperationKey()) \
.add_column(responseCodeKey()) \
.add_column(apiVersionKey()) \
.add_column(apiNameKey())
#define ADD_COMMON_TAGS \
.add_column(requestProtocolKey()) \
.add_column(serviceAuthenticationPolicyKey()) \
.add_column(meshUIDKey()) \
.add_column(destinationServiceNameKey()) \
.add_column(destinationServiceNamespaceKey()) \
.add_column(destinationPortKey()) \
.add_column(sourcePrincipalKey()) \
.add_column(sourceWorkloadNameKey()) \
.add_column(sourceWorkloadNamespaceKey()) \
.add_column(sourceOwnerKey()) \
.add_column(destinationPrincipalKey()) \
.add_column(destinationWorkloadNameKey()) \
.add_column(destinationWorkloadNamespaceKey()) \
.add_column(destinationOwnerKey()) \
.add_column(destinationCanonicalServiceNameKey()) \
.add_column(destinationCanonicalServiceNamespaceKey()) \
.add_column(sourceCanonicalServiceNameKey()) \
.add_column(sourceCanonicalServiceNamespaceKey()) \
.add_column(destinationCanonicalRevisionKey()) \
.add_column(sourceCanonicalRevisionKey()) \
.add_column(proxyVersionKey())
// Functions to register opencensus views to export.
REGISTER_COUNT_VIEW(ServerRequestCount)
REGISTER_BYTES_DISTRIBUTION_VIEW(ServerRequestBytes)
REGISTER_BYTES_DISTRIBUTION_VIEW(ServerResponseBytes)
REGISTER_DISTRIBUTION_VIEW(ServerResponseLatencies)
REGISTER_COUNT_VIEW(ClientRequestCount)
REGISTER_BYTES_DISTRIBUTION_VIEW(ClientRequestBytes)
REGISTER_BYTES_DISTRIBUTION_VIEW(ClientResponseBytes)
REGISTER_DISTRIBUTION_VIEW(ClientRoundtripLatencies)
REGISTER_TCP_COUNT_VIEW(ServerConnectionsOpenCount)
REGISTER_TCP_COUNT_VIEW(ServerConnectionsCloseCount)
REGISTER_TCP_SUM_VIEW(ServerReceivedBytesCount)
REGISTER_TCP_SUM_VIEW(ServerSentBytesCount)
REGISTER_TCP_COUNT_VIEW(ClientConnectionsOpenCount)
REGISTER_TCP_COUNT_VIEW(ClientConnectionsCloseCount)
REGISTER_TCP_SUM_VIEW(ClientReceivedBytesCount)
REGISTER_TCP_SUM_VIEW(ClientSentBytesCount)
/*
* measure function macros
*/
#define MEASURE_FUNC(_fn, _m, _u, _t) \
Measure##_t _fn##Measure() { \
static const Measure##_t measure = Measure##_t::Register(k##_m##Measure, "", #_u); \
return measure; \
}
// Meausre functions
MEASURE_FUNC(serverRequestCount, ServerRequestCount, 1, Int64)
MEASURE_FUNC(serverRequestBytes, ServerRequestBytes, By, Int64)
MEASURE_FUNC(serverResponseBytes, ServerResponseBytes, By, Int64)
MEASURE_FUNC(serverResponseLatencies, ServerResponseLatencies, ms, Double)
MEASURE_FUNC(clientRequestCount, ClientRequestCount, 1, Int64)
MEASURE_FUNC(clientRequestBytes, ClientRequestBytes, By, Int64)
MEASURE_FUNC(clientResponseBytes, ClientResponseBytes, By, Int64)
MEASURE_FUNC(clientRoundtripLatencies, ClientRoundtripLatencies, ms, Double)
MEASURE_FUNC(serverConnectionsOpenCount, ServerConnectionsOpenCount, 1, Int64)
MEASURE_FUNC(serverConnectionsCloseCount, ServerConnectionsCloseCount, 1, Int64)
MEASURE_FUNC(serverReceivedBytesCount, ServerReceivedBytesCount, By, Int64)
MEASURE_FUNC(serverSentBytesCount, ServerSentBytesCount, By, Int64)
MEASURE_FUNC(clientConnectionsOpenCount, ClientConnectionsOpenCount, 1, Int64)
MEASURE_FUNC(clientConnectionsCloseCount, ClientConnectionsCloseCount, 1, Int64)
MEASURE_FUNC(clientReceivedBytesCount, ClientReceivedBytesCount, By, Int64)
MEASURE_FUNC(clientSentBytesCount, ClientSentBytesCount, By, Int64)
void registerViews(absl::Duration expiry_duration,
const std::vector<std::string>& dropped_metrics) {
// Register measure first, which views depend on.
serverRequestCountMeasure();
serverRequestBytesMeasure();
serverResponseBytesMeasure();
serverResponseLatenciesMeasure();
clientRequestCountMeasure();
clientRequestBytesMeasure();
clientResponseBytesMeasure();
clientRoundtripLatenciesMeasure();
serverConnectionsOpenCountMeasure();
serverConnectionsCloseCountMeasure();
serverReceivedBytesCountMeasure();
serverSentBytesCountMeasure();
clientConnectionsOpenCountMeasure();
clientConnectionsCloseCountMeasure();
clientReceivedBytesCountMeasure();
clientSentBytesCountMeasure();
// Register views to export;
registerServerRequestCountView(expiry_duration, dropped_metrics);
registerServerRequestBytesView(expiry_duration, dropped_metrics);
registerServerResponseBytesView(expiry_duration, dropped_metrics);
registerServerResponseLatenciesView(expiry_duration, dropped_metrics);
registerClientRequestCountView(expiry_duration, dropped_metrics);
registerClientRequestBytesView(expiry_duration, dropped_metrics);
registerClientResponseBytesView(expiry_duration, dropped_metrics);
registerClientRoundtripLatenciesView(expiry_duration, dropped_metrics);
registerServerConnectionsOpenCountView(expiry_duration, dropped_metrics);
registerServerConnectionsCloseCountView(expiry_duration, dropped_metrics);
registerServerReceivedBytesCountView(expiry_duration, dropped_metrics);
registerServerSentBytesCountView(expiry_duration, dropped_metrics);
registerClientConnectionsOpenCountView(expiry_duration, dropped_metrics);
registerClientConnectionsCloseCountView(expiry_duration, dropped_metrics);
registerClientReceivedBytesCountView(expiry_duration, dropped_metrics);
registerClientSentBytesCountView(expiry_duration, dropped_metrics);
}
void dropViews(const std::vector<std::string>& dropped_metrics) {
for (const auto& metric : dropped_metrics) {
opencensus::stats::StatsExporter::RemoveView(metric);
}
}
/*
* tag key function macros
*/
#define TAG_KEY_FUNC(_t, _f) \
opencensus::tags::TagKey _f##Key() { \
static const auto _t##_key = opencensus::tags::TagKey::Register(#_t); \
return _t##_key; \
}
// Tag key functions
TAG_KEY_FUNC(response_code, responseCode)
TAG_KEY_FUNC(request_operation, requestOperation)
TAG_KEY_FUNC(request_protocol, requestProtocol)
TAG_KEY_FUNC(service_authentication_policy, serviceAuthenticationPolicy)
TAG_KEY_FUNC(mesh_uid, meshUID)
TAG_KEY_FUNC(destination_service_name, destinationServiceName)
TAG_KEY_FUNC(destination_service_namespace, destinationServiceNamespace)
TAG_KEY_FUNC(destination_port, destinationPort)
TAG_KEY_FUNC(response_code, desponseCode)
TAG_KEY_FUNC(source_principal, sourcePrincipal)
TAG_KEY_FUNC(source_workload_name, sourceWorkloadName)
TAG_KEY_FUNC(source_workload_namespace, sourceWorkloadNamespace)
TAG_KEY_FUNC(source_owner, sourceOwner)
TAG_KEY_FUNC(destination_principal, destinationPrincipal)
TAG_KEY_FUNC(destination_workload_name, destinationWorkloadName)
TAG_KEY_FUNC(destination_workload_namespace, destinationWorkloadNamespace)
TAG_KEY_FUNC(destination_owner, destinationOwner)
TAG_KEY_FUNC(source_canonical_service_name, sourceCanonicalServiceName)
TAG_KEY_FUNC(source_canonical_service_namespace, sourceCanonicalServiceNamespace)
TAG_KEY_FUNC(destination_canonical_service_name, destinationCanonicalServiceName)
TAG_KEY_FUNC(destination_canonical_service_namespace, destinationCanonicalServiceNamespace)
TAG_KEY_FUNC(source_canonical_revision, sourceCanonicalRevision)
TAG_KEY_FUNC(destination_canonical_revision, destinationCanonicalRevision)
TAG_KEY_FUNC(api_name, apiName)
TAG_KEY_FUNC(api_version, apiVersion)
TAG_KEY_FUNC(proxy_version, proxyVersion)
} // namespace Metric
} // namespace Stackdriver
} // namespace Extensions

View File

@ -0,0 +1,93 @@
/* 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 "extensions/common/context.h"
#include "extensions/stackdriver/common/utils.h"
// OpenCensus is full of unused parameters in metric_service.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include "opencensus/exporters/stats/stackdriver/stackdriver_exporter.h"
#pragma GCC diagnostic pop
#include "opencensus/stats/measure.h"
#include "opencensus/stats/stats.h"
#include "opencensus/stats/tag_key.h"
namespace Extensions {
namespace Stackdriver {
namespace Metric {
// Returns Stackdriver exporter config option based on node metadata.
opencensus::exporters::stats::StackdriverOptions
getStackdriverOptions(const Wasm::Common::FlatNode& local_node_info,
const ::Extensions::Stackdriver::Common::StackdriverStubOption& stub_option);
// registers Opencensus views
void registerViews(absl::Duration, const std::vector<std::string>&);
// drops existing OC views
void dropViews(const std::vector<std::string>&);
// Opencensus tag key functions.
opencensus::tags::TagKey requestOperationKey();
opencensus::tags::TagKey requestProtocolKey();
opencensus::tags::TagKey serviceAuthenticationPolicyKey();
opencensus::tags::TagKey meshUIDKey();
opencensus::tags::TagKey destinationServiceNameKey();
opencensus::tags::TagKey destinationServiceNamespaceKey();
opencensus::tags::TagKey destinationPortKey();
opencensus::tags::TagKey responseCodeKey();
opencensus::tags::TagKey sourcePrincipalKey();
opencensus::tags::TagKey sourceWorkloadNameKey();
opencensus::tags::TagKey sourceWorkloadNamespaceKey();
opencensus::tags::TagKey sourceOwnerKey();
opencensus::tags::TagKey destinationPrincipalKey();
opencensus::tags::TagKey destinationWorkloadNameKey();
opencensus::tags::TagKey destinationWorkloadNamespaceKey();
opencensus::tags::TagKey destinationOwnerKey();
opencensus::tags::TagKey destinationCanonicalServiceNameKey();
opencensus::tags::TagKey destinationCanonicalServiceNamespaceKey();
opencensus::tags::TagKey sourceCanonicalServiceNameKey();
opencensus::tags::TagKey sourceCanonicalServiceNamespaceKey();
opencensus::tags::TagKey destinationCanonicalRevisionKey();
opencensus::tags::TagKey sourceCanonicalRevisionKey();
opencensus::tags::TagKey apiNameKey();
opencensus::tags::TagKey apiVersionKey();
opencensus::tags::TagKey proxyVersionKey();
// Opencensus measure functions.
opencensus::stats::MeasureInt64 serverRequestCountMeasure();
opencensus::stats::MeasureInt64 serverRequestBytesMeasure();
opencensus::stats::MeasureInt64 serverResponseBytesMeasure();
opencensus::stats::MeasureDouble serverResponseLatenciesMeasure();
opencensus::stats::MeasureInt64 clientRequestCountMeasure();
opencensus::stats::MeasureInt64 clientRequestBytesMeasure();
opencensus::stats::MeasureInt64 clientResponseBytesMeasure();
opencensus::stats::MeasureDouble clientRoundtripLatenciesMeasure();
opencensus::stats::MeasureInt64 serverConnectionsOpenCountMeasure();
opencensus::stats::MeasureInt64 serverConnectionsCloseCountMeasure();
opencensus::stats::MeasureInt64 serverReceivedBytesCountMeasure();
opencensus::stats::MeasureInt64 serverSentBytesCountMeasure();
opencensus::stats::MeasureInt64 clientConnectionsOpenCountMeasure();
opencensus::stats::MeasureInt64 clientConnectionsCloseCountMeasure();
opencensus::stats::MeasureInt64 clientReceivedBytesCountMeasure();
opencensus::stats::MeasureInt64 clientSentBytesCountMeasure();
} // namespace Metric
} // namespace Stackdriver
} // namespace Extensions

View File

@ -0,0 +1,135 @@
/* 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 "extensions/stackdriver/metric/registry.h"
#include "extensions/stackdriver/common/constants.h"
#include "google/protobuf/util/message_differencer.h"
#include "gtest/gtest.h"
namespace Extensions {
namespace Stackdriver {
namespace Metric {
using google::protobuf::util::MessageDifferencer;
const ::Wasm::Common::FlatNode& nodeInfo(flatbuffers::FlatBufferBuilder& fbb) {
auto name = fbb.CreateString("test_pod");
auto namespace_ = fbb.CreateString("test_namespace");
std::vector<flatbuffers::Offset<::Wasm::Common::KeyVal>> platform_metadata = {
::Wasm::Common::CreateKeyVal(fbb, fbb.CreateString(Common::kGCPProjectKey),
fbb.CreateString("test_project")),
::Wasm::Common::CreateKeyVal(fbb, fbb.CreateString(Common::kGCPClusterNameKey),
fbb.CreateString("test_cluster")),
::Wasm::Common::CreateKeyVal(fbb, fbb.CreateString(Common::kGCPLocationKey),
fbb.CreateString("test_location"))};
auto platform_metadata_offset = fbb.CreateVectorOfSortedTables(&platform_metadata);
::Wasm::Common::FlatNodeBuilder node(fbb);
node.add_name(name);
node.add_namespace_(namespace_);
node.add_platform_metadata(platform_metadata_offset);
auto data = node.Finish();
fbb.Finish(data);
return *flatbuffers::GetRoot<::Wasm::Common::FlatNode>(fbb.GetBufferPointer());
}
const ::Wasm::Common::FlatNode& nodeInfoWithNoPlatform(flatbuffers::FlatBufferBuilder& fbb) {
auto name = fbb.CreateString("test_pod");
auto namespace_ = fbb.CreateString("test_namespace");
::Wasm::Common::FlatNodeBuilder node(fbb);
node.add_name(name);
node.add_namespace_(namespace_);
auto data = node.Finish();
fbb.Finish(data);
return *flatbuffers::GetRoot<::Wasm::Common::FlatNode>(fbb.GetBufferPointer());
}
google::api::MonitoredResource serverMonitoredResource() {
google::api::MonitoredResource monitored_resource;
monitored_resource.set_type(Common::kContainerMonitoredResource);
(*monitored_resource.mutable_labels())[Common::kProjectIDLabel] = "test_project";
(*monitored_resource.mutable_labels())[Common::kLocationLabel] = "test_location";
(*monitored_resource.mutable_labels())[Common::kClusterNameLabel] = "test_cluster";
(*monitored_resource.mutable_labels())[Common::kNamespaceNameLabel] = "test_namespace";
(*monitored_resource.mutable_labels())[Common::kPodNameLabel] = "test_pod";
(*monitored_resource.mutable_labels())[Common::kContainerNameLabel] = "istio-proxy";
return monitored_resource;
}
google::api::MonitoredResource clientMonitoredResource() {
google::api::MonitoredResource monitored_resource;
monitored_resource.set_type(Common::kPodMonitoredResource);
(*monitored_resource.mutable_labels())[Common::kProjectIDLabel] = "test_project";
(*monitored_resource.mutable_labels())[Common::kLocationLabel] = "test_location";
(*monitored_resource.mutable_labels())[Common::kClusterNameLabel] = "test_cluster";
(*monitored_resource.mutable_labels())[Common::kNamespaceNameLabel] = "test_namespace";
(*monitored_resource.mutable_labels())[Common::kPodNameLabel] = "test_pod";
return monitored_resource;
}
TEST(RegistryTest, getStackdriverOptionsProjectID) {
flatbuffers::FlatBufferBuilder fbb;
const auto& node_info = nodeInfo(fbb);
::Extensions::Stackdriver::Common::StackdriverStubOption stub_option;
auto options = getStackdriverOptions(node_info, stub_option);
EXPECT_EQ(options.project_id, "test_project");
}
TEST(RegistryTest, getStackdriverOptionsNoProjectID) {
flatbuffers::FlatBufferBuilder fbb;
const auto& node_info = nodeInfoWithNoPlatform(fbb);
::Extensions::Stackdriver::Common::StackdriverStubOption stub_option;
auto options = getStackdriverOptions(node_info, stub_option);
EXPECT_EQ(options.project_id, "");
}
TEST(RegistryTest, getStackdriverOptionsMonitoredResource) {
flatbuffers::FlatBufferBuilder fbb;
const auto& node_info = nodeInfo(fbb);
auto expected_server_monitored_resource = serverMonitoredResource();
auto expected_client_monitored_resource = clientMonitoredResource();
::Extensions::Stackdriver::Common::StackdriverStubOption stub_option;
auto options = getStackdriverOptions(node_info, stub_option);
EXPECT_EQ(options.project_id, "test_project");
EXPECT_TRUE(MessageDifferencer::Equals(
options.per_metric_monitored_resource.at(Common::kServerRequestCountView),
expected_server_monitored_resource));
EXPECT_TRUE(MessageDifferencer::Equals(
options.per_metric_monitored_resource.at(Common::kServerRequestBytesView),
expected_server_monitored_resource));
EXPECT_TRUE(MessageDifferencer::Equals(
options.per_metric_monitored_resource.at(Common::kServerResponseLatenciesView),
expected_server_monitored_resource));
EXPECT_TRUE(MessageDifferencer::Equals(
options.per_metric_monitored_resource.at(Common::kServerResponseBytesView),
expected_server_monitored_resource));
EXPECT_TRUE(MessageDifferencer::Equals(
options.per_metric_monitored_resource.at(Common::kClientRequestCountView),
expected_client_monitored_resource));
EXPECT_TRUE(MessageDifferencer::Equals(
options.per_metric_monitored_resource.at(Common::kClientRequestBytesView),
expected_client_monitored_resource));
EXPECT_TRUE(MessageDifferencer::Equals(
options.per_metric_monitored_resource.at(Common::kClientResponseBytesView),
expected_client_monitored_resource));
EXPECT_TRUE(MessageDifferencer::Equals(
options.per_metric_monitored_resource.at(Common::kClientRoundtripLatenciesView),
expected_client_monitored_resource));
}
} // namespace Metric
} // namespace Stackdriver
} // namespace Extensions

View File

@ -0,0 +1,842 @@
/* 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 "extensions/stackdriver/stackdriver.h"
#include <google/protobuf/util/json_util.h>
#include <random>
#include <string>
#include <unordered_map>
#include "extensions/common/proto_util.h"
#include "extensions/common/util.h"
#include "extensions/stackdriver/log/exporter.h"
#include "extensions/stackdriver/metric/registry.h"
#include "google/protobuf/util/time_util.h"
#ifndef NULL_PLUGIN
#include "api/wasm/cpp/proxy_wasm_intrinsics.h"
#else
#include "include/proxy-wasm/null_plugin.h"
namespace proxy_wasm {
namespace null_plugin {
#endif
#include "contrib/proxy_expr.h"
namespace Stackdriver {
using namespace opencensus::exporters::stats;
using namespace google::protobuf::util;
using namespace ::Extensions::Stackdriver::Common;
using namespace ::Extensions::Stackdriver::Metric;
using Extensions::Stackdriver::Log::ExporterImpl;
using ::Extensions::Stackdriver::Log::Logger;
using stackdriver::config::v1alpha1::PluginConfig;
using ::Wasm::Common::kDownstreamMetadataIdKey;
using ::Wasm::Common::kDownstreamMetadataKey;
using ::Wasm::Common::kUpstreamMetadataIdKey;
using ::Wasm::Common::kUpstreamMetadataKey;
using ::Wasm::Common::RequestInfo;
using ::Wasm::Common::TCPConnectionState;
constexpr char kStackdriverExporter[] = "stackdriver_exporter";
constexpr char kExporterRegistered[] = "registered";
constexpr int kDefaultTickerMilliseconds = 10000; // 10s
namespace {
constexpr char kRbacAccessAllowed[] = "AuthzAllowed";
constexpr char kRbacAccessDenied[] = "AuthzDenied";
constexpr char kRBACHttpFilterName[] = "envoy.filters.http.rbac";
constexpr char kRBACNetworkFilterName[] = "envoy.filters.network.rbac";
constexpr char kDryRunDenyShadowEngineResult[] = "istio_dry_run_deny_shadow_engine_result";
constexpr char kDryRunAllowShadowEngineResult[] = "istio_dry_run_allow_shadow_engine_result";
constexpr char kDryRunDenyShadowEffectiveId[] = "istio_dry_run_deny_shadow_effective_policy_id";
constexpr char kDryRunAllowShadowEffectiveId[] = "istio_dry_run_allow_shadow_effective_policy_id";
// Get metric export interval from node metadata. Returns 60 seconds if interval
// is not found in metadata.
int getMonitoringExportInterval() {
std::string interval_s = "";
if (getValue({"node", "metadata", kMonitoringExportIntervalKey}, &interval_s)) {
return std::stoi(interval_s);
}
return 60;
}
// Get proxy timer interval from node metadata in milliseconds. Returns 10
// seconds if interval is not found in metadata.
int getProxyTickerIntervalMilliseconds() {
std::string interval_s = "";
if (getValue({"node", "metadata", kProxyTickerIntervalKey}, &interval_s)) {
return std::stoi(interval_s) * 1000;
}
return kDefaultTickerMilliseconds;
}
// Get logging export interval from node metadata in nanoseconds. Returns 60
// seconds if interval is not found in metadata.
long int getTcpLogEntryTimeoutNanoseconds() {
std::string interval_s = "";
if (getValue({"node", "metadata", kTcpLogEntryTimeoutKey}, &interval_s)) {
return std::stoi(interval_s) * 1000000000;
}
return kDefaultTcpLogEntryTimeoutNanoseconds;
}
// Get port of security token exchange server from node metadata, if not
// provided or "0" is provided, emtpy will be returned.
std::string getSTSPort() {
std::string sts_port;
if (getValue({"node", "metadata", kSTSPortKey}, &sts_port) && sts_port != "0") {
return sts_port;
}
return "";
}
// Get file name for the token test override.
std::string getTokenFile() {
std::string token_file;
if (!getValue({"node", "metadata", kTokenFile}, &token_file)) {
return "";
}
return token_file;
}
// Get file name for the root CA PEM file test override.
std::string getCACertFile() {
std::string ca_cert_file;
if (!getValue({"node", "metadata", kCACertFile}, &ca_cert_file)) {
return "";
}
return ca_cert_file;
}
// Get secure stackdriver endpoint for e2e testing.
std::string getSecureEndpoint() {
std::string secure_endpoint;
if (!getValue({"node", "metadata", kSecureStackdriverEndpointKey}, &secure_endpoint)) {
return "";
}
return secure_endpoint;
}
// Get insecure stackdriver endpoint for e2e testing.
std::string getInsecureEndpoint() {
std::string insecure_endpoint;
if (!getValue({"node", "metadata", kInsecureStackdriverEndpointKey}, &insecure_endpoint)) {
return "";
}
return insecure_endpoint;
}
// Get GCP monitoring endpoint. When this is provided, it will override the
// default production endpoint. This should be used to test staging monitoring
// endpoint.
std::string getMonitoringEndpoint() {
std::string monitoring_endpoint;
if (!getValue({"node", "metadata", kMonitoringEndpointKey}, &monitoring_endpoint)) {
return "";
}
return monitoring_endpoint;
}
// Get GCP project number.
std::string getProjectNumber() {
std::string project_number;
if (!getValue({"node", "metadata", "PLATFORM_METADATA", kGCPProjectNumberKey}, &project_number)) {
return "";
}
return project_number;
}
absl::Duration getMetricExpiryDuration(const stackdriver::config::v1alpha1::PluginConfig& config) {
if (!config.has_metric_expiry_duration()) {
return absl::ZeroDuration();
}
auto& duration = config.metric_expiry_duration();
return absl::Seconds(duration.seconds()) + absl::Nanoseconds(duration.nanos());
}
std::vector<std::string>
getDroppedMetrics(const stackdriver::config::v1alpha1::PluginConfig& config) {
std::vector<std::string> dropped_metrics;
for (const auto& override : config.metrics_overrides()) {
if (override.second.drop()) {
dropped_metrics.push_back(override.first);
}
}
return dropped_metrics;
}
bool isAllowedOverride(std::string metric, std::string tag) {
for (const auto& label : kDefinedLabels) {
if (label == tag) {
return true;
}
}
if (absl::StrContains(metric, "connection_") || absl::StrContains(metric, "bytes_count")) {
// short-circuit for TCP metrics
return false;
}
for (const auto& label : kHttpDefinedLabels) {
if (label == tag) {
return true;
}
}
return false;
}
void clearTcpMetrics(::Wasm::Common::RequestInfo& request_info) {
request_info.tcp_connections_opened = 0;
request_info.tcp_sent_bytes = 0;
request_info.tcp_received_bytes = 0;
}
// Get local node metadata. If mesh id is not filled or does not exist,
// fall back to default format `proj-<project-number>`.
flatbuffers::DetachedBuffer getLocalNodeMetadata() {
google::protobuf::Struct node;
auto local_node_info = ::Wasm::Common::extractLocalNodeFlatBuffer();
::Wasm::Common::extractStructFromNodeFlatBuffer(
*flatbuffers::GetRoot<::Wasm::Common::FlatNode>(local_node_info.data()), &node);
const auto mesh_id_it = node.fields().find("MESH_ID");
if (mesh_id_it != node.fields().end() && !mesh_id_it->second.string_value().empty() &&
absl::StartsWith(mesh_id_it->second.string_value(), "proj-")) {
// do nothing
} else {
// Insert or update mesh id to default format as it is missing, empty, or
// not properly set.
auto project_number = getProjectNumber();
auto* mesh_id_field = (*node.mutable_fields())["MESH_ID"].mutable_string_value();
if (!project_number.empty()) {
*mesh_id_field = absl::StrCat("proj-", project_number);
}
}
return ::Wasm::Common::extractNodeFlatBufferFromStruct(node);
}
bool extractAuthzPolicyName(const std::string& policy, std::string& out_namespace,
std::string& out_name, std::string& out_rule) {
// The policy has format "ns[foo]-policy[httpbin-deny]-rule[0]".
if (absl::StartsWith(policy, "ns[") && absl::EndsWith(policy, "]")) {
std::string sepPolicy = "]-policy[";
std::size_t beginNs = 3;
std::size_t endNs = policy.find(sepPolicy, beginNs);
if (endNs == std::string::npos) {
return false;
}
std::string sepNs = "]-rule[";
std::size_t beginName = endNs + sepPolicy.size();
std::size_t endName = policy.find(sepNs, beginName);
if (endName == std::string::npos) {
return false;
}
std::size_t beginRule = endName + sepNs.size();
std::size_t endRule = policy.size() - 1;
out_namespace = policy.substr(beginNs, endNs - beginNs);
out_name = policy.substr(beginName, endName - beginName);
out_rule = policy.substr(beginRule, endRule - beginRule);
return true;
}
return false;
}
void fillAuthzDryRunInfo(std::unordered_map<std::string, std::string>& extra_labels) {
auto md = getProperty({"metadata", "filter_metadata", kRBACHttpFilterName});
if (!md.has_value()) {
md = getProperty({"metadata", "filter_metadata", kRBACNetworkFilterName});
if (!md.has_value()) {
LOG_DEBUG("RBAC metadata not found");
return;
}
}
bool shadow_deny_result = true;
bool shadow_allow_result = true;
bool has_shadow_metadata = false;
std::string shadow_deny_policy = "";
std::string shadow_allow_policy = "";
for (const auto& [key, val] : md.value()->pairs()) {
LOG_DEBUG(absl::StrCat("RBAC metadata found: key=", Wasm::Common::toAbslStringView(key),
", value=", Wasm::Common::toAbslStringView(val)));
if (key == kDryRunDenyShadowEngineResult) {
shadow_deny_result = (val == "allowed");
} else if (key == kDryRunAllowShadowEngineResult) {
shadow_allow_result = (val == "allowed");
} else if (key == kDryRunDenyShadowEffectiveId) {
shadow_deny_policy = val;
} else if (key == kDryRunAllowShadowEffectiveId) {
shadow_allow_policy = val;
} else {
continue;
}
has_shadow_metadata = true;
}
if (!has_shadow_metadata) {
LOG_DEBUG("RBAC dry-run metadata not found");
return;
}
bool shadow_result = false;
std::string shadow_effective_policy = "";
if (shadow_deny_result && shadow_allow_result) {
// If allowed by both DENY and ALLOW policy, the final shadow_result should
// be true (allow) and the shadow_effective_policy should be from the ALLOW
// policy.
shadow_result = true;
shadow_effective_policy = shadow_allow_policy;
LOG_DEBUG("RBAC dry-run result: allowed");
} else {
// If denied by either DENY or ALLOW policy, the final shadow_reulst should
// be false (denied).
shadow_result = false;
if (!shadow_deny_result) {
// If denied by DENY policy, the shadow_effective_policy should be from
// the DENY policy.
shadow_effective_policy = shadow_deny_policy;
LOG_DEBUG("RBAC dry-run result: denied by DENY policy");
} else {
// If denied by ALLOW policy, the shadow_effective_policy shold be from
// the ALLOW policy.
shadow_effective_policy = shadow_allow_policy;
LOG_DEBUG("RBAC dry-run result: denied by ALLOW policy");
}
}
extra_labels["dry_run_result"] = shadow_result ? kRbacAccessAllowed : kRbacAccessDenied;
std::string policy_namespace = "";
std::string policy_name = "";
std::string policy_rule = "";
if (extractAuthzPolicyName(shadow_effective_policy, policy_namespace, policy_name, policy_rule)) {
extra_labels["dry_run_policy_name"] = absl::StrCat(policy_namespace, ".", policy_name);
extra_labels["dry_run_policy_rule"] = policy_rule;
LOG_DEBUG(absl::StrCat("RBAC dry-run matched policy: ns=", policy_namespace,
", name=", policy_name, ", rule=", policy_rule));
}
}
} // namespace
// onConfigure == false makes the proxy crash.
// Only policy plugins should return false.
bool StackdriverRootContext::onConfigure(size_t size) {
initialized_ = configure(size);
return true;
}
bool StackdriverRootContext::initializeLogFilter() {
uint32_t token = 0;
if (config_.access_logging_filter_expression() == "") {
log_filter_token_ = token;
return true;
}
if (createExpression(config_.access_logging_filter_expression(), &token) != WasmResult::Ok) {
LOG_TRACE(absl::StrCat("cannot create an filter expression: " +
config_.access_logging_filter_expression()));
return false;
}
log_filter_token_ = token;
return true;
}
bool StackdriverRootContext::configure(size_t configuration_size) {
// onStart is called prior to onConfigure
int proxy_tick_ms = getProxyTickerIntervalMilliseconds();
proxy_set_tick_period_milliseconds(getProxyTickerIntervalMilliseconds());
// Parse configuration JSON string.
std::string configuration = "{}";
if (configuration_size > 0) {
auto configuration_data =
getBufferBytes(WasmBufferType::PluginConfiguration, 0, configuration_size);
configuration = configuration_data->toString();
}
// TODO: add config validation to reject the listener if project id is not in
// metadata. Parse configuration JSON string.
JsonParseOptions json_options;
json_options.ignore_unknown_fields = true;
const auto status = JsonStringToMessage(configuration, &config_, json_options);
if (!status.ok()) {
logWarn("Cannot parse Stackdriver plugin configuration JSON string " + configuration + ", " +
std::string(status.message()));
return false;
}
local_node_info_ = getLocalNodeMetadata();
if (config_.has_log_report_duration()) {
log_report_duration_nanos_ =
::google::protobuf::util::TimeUtil::DurationToNanoseconds(config_.log_report_duration());
long int proxy_tick_ns = proxy_tick_ms * 1000;
if (log_report_duration_nanos_ < (proxy_tick_ns) ||
log_report_duration_nanos_ % proxy_tick_ns != 0) {
logWarn(absl::StrCat("The duration set is less than or not a multiple of default timer's "
"period. Default Timer MS: ",
proxy_tick_ms,
" Lod Duration Nanosecond: ", log_report_duration_nanos_));
}
}
direction_ = ::Wasm::Common::getTrafficDirection();
use_host_header_fallback_ = !config_.disable_host_header_fallback();
const ::Wasm::Common::FlatNode& local_node =
*flatbuffers::GetRoot<::Wasm::Common::FlatNode>(local_node_info_.data());
// Common stackdriver stub option for logging and monitoring.
::Extensions::Stackdriver::Common::StackdriverStubOption stub_option;
stub_option.sts_port = getSTSPort();
stub_option.test_token_path = getTokenFile();
stub_option.test_root_pem_path = getCACertFile();
stub_option.secure_endpoint = getSecureEndpoint();
stub_option.insecure_endpoint = getInsecureEndpoint();
stub_option.monitoring_endpoint = getMonitoringEndpoint();
stub_option.enable_log_compression =
config_.has_enable_log_compression() && config_.enable_log_compression().value();
const auto platform_metadata = local_node.platform_metadata();
if (platform_metadata) {
const auto project_iter = platform_metadata->LookupByKey(kGCPProjectKey);
if (project_iter) {
stub_option.project_id = flatbuffers::GetString(project_iter->value());
}
}
if (enableAccessLog()) {
std::unordered_map<std::string, std::string> extra_labels;
cleanupExpressions();
cleanupLogFilter();
if (!initializeLogFilter()) {
LOG_WARN("Could not build filter expression for logging.");
}
if (config_.has_custom_log_config()) {
for (const auto& dimension : config_.custom_log_config().dimensions()) {
uint32_t token;
if (createExpression(dimension.second, &token) != WasmResult::Ok) {
LOG_TRACE(absl::StrCat("Could not create expression for ", dimension.second));
continue;
}
expressions_.push_back({token, dimension.first, dimension.second});
}
}
// logger should only be initiated once, for now there is no reason to
// recreate logger because of config update.
if (!logger_) {
auto logging_stub_option = stub_option;
logging_stub_option.default_endpoint = kLoggingService;
auto exporter = std::make_unique<ExporterImpl>(this, logging_stub_option);
// logger takes ownership of exporter.
if (config_.max_log_batch_size_in_bytes() > 0) {
logger_ = std::make_unique<Logger>(local_node, std::move(exporter), extra_labels,
config_.max_log_batch_size_in_bytes());
} else {
logger_ = std::make_unique<Logger>(local_node, std::move(exporter), extra_labels);
}
}
tcp_log_entry_timeout_ = getTcpLogEntryTimeoutNanoseconds();
}
// Extract metric tags expressions
cleanupMetricsExpressions();
for (const auto& override : config_.metrics_overrides()) {
for (const auto& tag : override.second.tag_overrides()) {
if (!isAllowedOverride(override.first, tag.first)) {
LOG_WARN(absl::StrCat("cannot use tag \"", tag.first, "\" in metric \"", override.first,
"\"; ignoring override"));
continue;
}
uint32_t token;
if (createExpression(tag.second, &token) != WasmResult::Ok) {
LOG_WARN(absl::StrCat("Could not create expression: \"", tag.second, "\" for tag \"",
tag.first, "\" on metric \"", override.first,
"\"; ignoring override"));
continue;
}
const auto& tag_key = ::opencensus::tags::TagKey::Register(tag.first);
metrics_expressions_.push_back({token, override.first, tag_key, tag.second});
}
}
// Register OC Stackdriver exporter and views to be exported.
// Note exporter and views are global singleton so they should only be
// registered once.
WasmDataPtr registered;
if (WasmResult::Ok == getSharedData(kStackdriverExporter, &registered)) {
return true;
}
setSharedData(kStackdriverExporter, kExporterRegistered);
auto monitoring_stub_option = stub_option;
monitoring_stub_option.default_endpoint = kMonitoringService;
opencensus::exporters::stats::StackdriverExporter::Register(
getStackdriverOptions(local_node, monitoring_stub_option));
opencensus::stats::StatsExporter::SetInterval(absl::Seconds(getMonitoringExportInterval()));
// Register opencensus measures and views.
auto dropped = getDroppedMetrics(config_);
dropViews(dropped);
registerViews(getMetricExpiryDuration(config_), dropped);
return true;
}
bool StackdriverRootContext::onStart(size_t) { return true; }
void StackdriverRootContext::onTick() {
auto cur = static_cast<long int>(getCurrentTimeNanoseconds());
for (auto const& item : tcp_request_queue_) {
// requestinfo is null, so continue.
if (item.second == nullptr) {
continue;
}
Context* context = getContext(item.first);
if (context == nullptr) {
continue;
}
context->setEffectiveContext();
if (recordTCP(item.first)) {
// Clear existing data in TCP metrics, so that we don't double count the
// metrics.
clearTcpMetrics(*(item.second->request_info));
}
}
if (enableAccessLog() && (cur - last_log_report_call_nanos_ > log_report_duration_nanos_)) {
logger_->exportLogEntry(/* is_on_done= */ false);
last_log_report_call_nanos_ = cur;
}
}
bool StackdriverRootContext::onDone() {
bool done = true;
// Check if logger is empty. In base Wasm VM, only onStart and onDone are
// called, but onConfigure is not triggered. onConfigure is only triggered in
// thread local VM, which makes it possible that logger_ is empty ptr even
// when logging is enabled.
if (logger_ && enableAccessLog() && logger_->exportLogEntry(/* is_on_done= */ true)) {
done = false;
}
for (auto const& item : tcp_request_queue_) {
// requestinfo is null, so continue.
if (item.second == nullptr) {
continue;
}
recordTCP(item.first);
}
tcp_request_queue_.clear();
cleanupExpressions();
cleanupMetricsExpressions();
cleanupLogFilter();
return done;
}
void StackdriverRootContext::record() {
const bool outbound = isOutbound();
Wasm::Common::PeerNodeInfo peer_node_info(
{outbound ? kUpstreamMetadataIdKey : kDownstreamMetadataIdKey},
{outbound ? kUpstreamMetadataKey : kDownstreamMetadataKey});
const ::Wasm::Common::FlatNode& local_node = getLocalNode();
::Wasm::Common::RequestInfo request_info;
::Wasm::Common::populateHTTPRequestInfo(outbound, useHostHeaderFallback(), &request_info);
override_map overrides;
evaluateMetricsExpressions(overrides);
::Extensions::Stackdriver::Metric::record(outbound, local_node, peer_node_info.get(),
request_info, !config_.disable_http_size_metrics(),
overrides);
bool extended_info_populated = false;
if ((enableAllAccessLog() ||
(enableAccessLogOnError() && (request_info.response_code >= 400 ||
request_info.response_flag != ::Wasm::Common::NONE))) &&
shouldLogThisRequest(request_info) && evaluateLogFilter()) {
::Wasm::Common::populateExtendedHTTPRequestInfo(&request_info);
std::unordered_map<std::string, std::string> extra_labels;
evaluateExpressions(extra_labels);
extended_info_populated = true;
fillAuthzDryRunInfo(extra_labels);
logger_->addLogEntry(request_info, peer_node_info.get(), extra_labels, outbound,
false /* audit */);
}
// TODO(dougreid): should Audits override log filters? I believe so. At this
// time, we won't apply logging filters to audit logs.
if (enableAuditLog() && shouldAuditThisRequest()) {
if (!extended_info_populated) {
::Wasm::Common::populateExtendedHTTPRequestInfo(&request_info);
}
logger_->addLogEntry(request_info, peer_node_info.get(), {}, outbound, true /* audit */);
}
}
bool StackdriverRootContext::recordTCP(uint32_t id) {
const bool outbound = isOutbound();
Wasm::Common::PeerNodeInfo peer_node_info(
{outbound ? kUpstreamMetadataIdKey : kDownstreamMetadataIdKey},
{outbound ? kUpstreamMetadataKey : kDownstreamMetadataKey});
const ::Wasm::Common::FlatNode& local_node = getLocalNode();
auto req_iter = tcp_request_queue_.find(id);
if (req_iter == tcp_request_queue_.end() || req_iter->second == nullptr) {
return false;
}
StackdriverRootContext::TcpRecordInfo& record_info = *(req_iter->second);
::Wasm::Common::RequestInfo& request_info = *(record_info.request_info);
// For TCP, if peer metadata is not available, peer id is set as not found.
// Otherwise, we wait for metadata exchange to happen before we report any
// metric before a timeout.
// We keep waiting if response flags is zero, as that implies, there has
// been no error in connection.
uint64_t response_flags = 0;
getValue({"response", "flags"}, &response_flags);
auto cur = static_cast<long int>(proxy_wasm::null_plugin::getCurrentTimeNanoseconds());
bool waiting_for_metadata = peer_node_info.maybeWaiting();
bool no_error = response_flags == 0;
bool log_open_on_timeout = !record_info.tcp_open_entry_logged &&
(cur - request_info.start_time) > tcp_log_entry_timeout_;
if (waiting_for_metadata && no_error && !log_open_on_timeout) {
return false;
}
if (!request_info.is_populated) {
::Wasm::Common::populateTCPRequestInfo(outbound, &request_info);
}
// Record TCP Metrics.
override_map overrides;
evaluateMetricsExpressions(overrides);
::Extensions::Stackdriver::Metric::recordTCP(outbound, local_node, peer_node_info.get(),
request_info, overrides);
bool extended_info_populated = false;
// Add LogEntry to Logger. Log Entries are batched and sent on timer
// to Stackdriver Logging Service.
if (!record_info.log_filter_evaluated) {
record_info.log_connection = evaluateLogFilter();
record_info.log_filter_evaluated = true;
}
if ((enableAllAccessLog() || (enableAccessLogOnError() && !no_error)) &&
record_info.log_connection) {
::Wasm::Common::populateExtendedRequestInfo(&request_info);
extended_info_populated = true;
if (!record_info.expressions_evaluated) {
evaluateExpressions(record_info.extra_log_labels);
record_info.expressions_evaluated = true;
}
fillAuthzDryRunInfo(record_info.extra_log_labels);
// It's possible that for a short lived TCP connection, we log TCP
// Connection Open log entry on connection close.
if (!record_info.tcp_open_entry_logged &&
request_info.tcp_connection_state == ::Wasm::Common::TCPConnectionState::Close) {
record_info.request_info->tcp_connection_state = ::Wasm::Common::TCPConnectionState::Open;
logger_->addTcpLogEntry(*record_info.request_info, peer_node_info.get(),
record_info.extra_log_labels, record_info.request_info->start_time,
outbound, false /* audit */);
record_info.request_info->tcp_connection_state = ::Wasm::Common::TCPConnectionState::Close;
}
logger_->addTcpLogEntry(request_info, peer_node_info.get(), record_info.extra_log_labels,
getCurrentTimeNanoseconds(), outbound, false /* audit */);
}
// TODO(dougreid): confirm that audit should override filtering.
if (enableAuditLog() && shouldAuditThisRequest()) {
if (!extended_info_populated) {
::Wasm::Common::populateExtendedRequestInfo(&request_info);
}
// It's possible that for a short lived TCP connection, we audit log TCP
// Connection Open log entry on connection close.
if (!record_info.tcp_open_entry_logged &&
request_info.tcp_connection_state == ::Wasm::Common::TCPConnectionState::Close) {
record_info.request_info->tcp_connection_state = ::Wasm::Common::TCPConnectionState::Open;
logger_->addTcpLogEntry(*record_info.request_info, peer_node_info.get(), {},
record_info.request_info->start_time, outbound, true /* audit */);
record_info.request_info->tcp_connection_state = ::Wasm::Common::TCPConnectionState::Close;
}
logger_->addTcpLogEntry(*record_info.request_info, peer_node_info.get(), {},
record_info.request_info->start_time, outbound, true /* audit */);
}
if (log_open_on_timeout) {
// If we logged the request on timeout, for outbound requests, we try to
// populate the request info again when metadata is available.
request_info.is_populated = outbound ? false : true;
}
if (!record_info.tcp_open_entry_logged) {
record_info.tcp_open_entry_logged = true;
}
return true;
}
inline bool StackdriverRootContext::isOutbound() {
return direction_ == ::Wasm::Common::TrafficDirection::Outbound;
}
inline bool StackdriverRootContext::enableAccessLog() {
return enableAllAccessLog() || enableAccessLogOnError();
}
inline bool StackdriverRootContext::enableAllAccessLog() {
// TODO(gargnupur): Remove (!config_.disable_server_access_logging() &&
// !isOutbound) once disable_server_access_logging config is removed.
return (!config_.disable_server_access_logging() && !isOutbound()) ||
config_.access_logging() == stackdriver::config::v1alpha1::PluginConfig::FULL;
}
inline bool StackdriverRootContext::evaluateLogFilter() {
if (config_.access_logging_filter_expression() == "") {
return true;
}
bool value;
if (!evaluateExpression(log_filter_token_, &value)) {
LOG_TRACE(absl::StrCat("Could not evaluate expression: ",
config_.access_logging_filter_expression()));
return true;
}
return value;
}
inline bool StackdriverRootContext::enableAccessLogOnError() {
return config_.access_logging() == stackdriver::config::v1alpha1::PluginConfig::ERRORS_ONLY;
}
inline bool StackdriverRootContext::enableAuditLog() { return config_.enable_audit_log(); }
bool StackdriverRootContext::shouldLogThisRequest(::Wasm::Common::RequestInfo& request_info) {
std::string shouldLog = "";
if (!getValue({::Wasm::Common::kAccessLogPolicyKey}, &shouldLog)) {
LOG_DEBUG("cannot get envoy access log info from filter state.");
return true;
}
// Add label log_sampled if Access Log Policy sampling was applied to logs.
request_info.log_sampled = (shouldLog != "no");
return request_info.log_sampled;
}
bool StackdriverRootContext::shouldAuditThisRequest() { return Wasm::Common::getAuditPolicy(); }
void StackdriverRootContext::addToTCPRequestQueue(uint32_t id) {
std::unique_ptr<::Wasm::Common::RequestInfo> request_info =
std::make_unique<::Wasm::Common::RequestInfo>();
request_info->tcp_connections_opened++;
request_info->start_time =
static_cast<long int>(proxy_wasm::null_plugin::getCurrentTimeNanoseconds());
std::unique_ptr<StackdriverRootContext::TcpRecordInfo> record_info =
std::make_unique<StackdriverRootContext::TcpRecordInfo>();
record_info->request_info = std::move(request_info);
record_info->tcp_open_entry_logged = false;
tcp_request_queue_[id] = std::move(record_info);
}
void StackdriverRootContext::deleteFromTCPRequestQueue(uint32_t id) {
tcp_request_queue_.erase(id);
}
void StackdriverRootContext::incrementReceivedBytes(uint32_t id, size_t size) {
tcp_request_queue_[id]->request_info->tcp_received_bytes += size;
tcp_request_queue_[id]->request_info->tcp_total_received_bytes += size;
}
void StackdriverRootContext::incrementSentBytes(uint32_t id, size_t size) {
tcp_request_queue_[id]->request_info->tcp_sent_bytes += size;
tcp_request_queue_[id]->request_info->tcp_total_sent_bytes += size;
}
void StackdriverRootContext::incrementConnectionClosed(uint32_t id) {
tcp_request_queue_[id]->request_info->tcp_connections_closed++;
}
void StackdriverRootContext::setConnectionState(uint32_t id,
::Wasm::Common::TCPConnectionState state) {
tcp_request_queue_[id]->request_info->tcp_connection_state = state;
}
void StackdriverRootContext::evaluateExpressions(
std::unordered_map<std::string, std::string>& extra_labels) {
for (const auto& expression : expressions_) {
std::string value;
if (!evaluateExpression(expression.token, &value)) {
LOG_TRACE(absl::StrCat("Could not evaluate expression: ", expression.expression));
continue;
}
extra_labels[expression.tag] = value;
}
}
void StackdriverRootContext::evaluateMetricsExpressions(override_map& overrides) {
for (const auto& expression : metrics_expressions_) {
std::string value;
if (!evaluateExpression(expression.token, &value)) {
LOG_WARN(absl::StrCat("Could not evaluate expression: ", expression.expression));
continue;
}
overrides[expression.metric].emplace_back(expression.tag, value);
}
}
void StackdriverRootContext::cleanupExpressions() {
for (const auto& expression : expressions_) {
exprDelete(expression.token);
}
expressions_.clear();
}
void StackdriverRootContext::cleanupMetricsExpressions() {
for (const auto& expression : metrics_expressions_) {
exprDelete(expression.token);
}
metrics_expressions_.clear();
}
void StackdriverRootContext::cleanupLogFilter() {
exprDelete(log_filter_token_);
log_filter_token_ = 0;
}
// TODO(bianpengyuan) Add final export once root context supports onDone.
// https://github.com/envoyproxy/envoy-wasm/issues/240
StackdriverRootContext* StackdriverContext::getRootContext() {
RootContext* root = this->root();
return dynamic_cast<StackdriverRootContext*>(root);
}
void StackdriverContext::onLog() {
if (!is_initialized_) {
return;
}
if (is_tcp_) {
getRootContext()->incrementConnectionClosed(context_id_);
getRootContext()->setConnectionState(context_id_, ::Wasm::Common::TCPConnectionState::Close);
getRootContext()->recordTCP(context_id_);
getRootContext()->deleteFromTCPRequestQueue(context_id_);
return;
}
// Record telemetry based on request info.
getRootContext()->record();
}
} // namespace Stackdriver
#ifdef NULL_PLUGIN
} // namespace null_plugin
} // namespace proxy_wasm
#endif

View File

@ -0,0 +1,270 @@
/* 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 "extensions/common/context.h"
#include "extensions/stackdriver/common/constants.h"
#include "extensions/stackdriver/config/v1alpha1/stackdriver_plugin_config.pb.h"
#include "extensions/stackdriver/log/logger.h"
#include "extensions/stackdriver/metric/record.h"
// OpenCensus is full of unused parameters in metric_service.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include "opencensus/exporters/stats/stackdriver/stackdriver_exporter.h"
#pragma GCC diagnostic pop
#ifndef NULL_PLUGIN
#include "api/wasm/cpp/proxy_wasm_intrinsics.h"
#else
#include "include/proxy-wasm/null_plugin.h"
namespace proxy_wasm {
namespace null_plugin {
#endif
namespace Stackdriver {
// 10m
constexpr long int kDefaultTcpLogEntryTimeoutNanoseconds = 60000000000; // 1m
constexpr long int kDefaultLogExportNanoseconds = 10000000000; // 10s
#ifdef NULL_PLUGIN
PROXY_WASM_NULL_PLUGIN_REGISTRY;
#endif
// StackdriverRootContext is the root context for all streams processed by the
// thread. It has the same lifetime as the worker thread and acts as target for
// interactions that outlives individual stream, e.g. timer, async calls.
class StackdriverRootContext : public RootContext {
public:
StackdriverRootContext(uint32_t id, std::string_view root_id) : RootContext(id, root_id) {
empty_node_info_ = ::Wasm::Common::extractEmptyNodeFlatBuffer();
}
~StackdriverRootContext() = default;
bool onConfigure(size_t) override;
bool configure(size_t);
bool onStart(size_t) override;
void onTick() override;
bool onDone() override;
// Get direction of traffic relative to this proxy.
bool isOutbound();
bool useHostHeaderFallback() const { return use_host_header_fallback_; };
// Records telemetry for the current active stream/connection. Returns true,
// if request was recorded.
bool recordTCP(uint32_t id);
// Records telemetry for the current active stream.
void record();
// Functions for TCP connection's RequestInfo queue.
void addToTCPRequestQueue(uint32_t id);
void deleteFromTCPRequestQueue(uint32_t id);
void incrementReceivedBytes(uint32_t id, size_t size);
void incrementSentBytes(uint32_t id, size_t size);
void incrementConnectionClosed(uint32_t id);
void setConnectionState(uint32_t id, ::Wasm::Common::TCPConnectionState state);
const ::Wasm::Common::FlatNode& getLocalNode() {
return *flatbuffers::GetRoot<::Wasm::Common::FlatNode>(local_node_info_.data());
}
bool initialized() const { return initialized_; };
private:
// Stores information about TCP request.
struct TcpRecordInfo {
std::unique_ptr<::Wasm::Common::RequestInfo> request_info;
bool tcp_open_entry_logged;
// This caches evaluated extra access log labels.
std::unordered_map<std::string, std::string> extra_log_labels;
bool expressions_evaluated;
// cache filter expression value
bool log_connection;
bool log_filter_evaluated;
};
// Indicates whether to export any kind of access log or not.
bool enableAccessLog();
// Indicates whether to export all server/client access log or not.
bool enableAllAccessLog();
// Indicates whether to export any access log or not when there was an
// error in request/connection.
bool enableAccessLogOnError();
bool shouldLogThisRequest(::Wasm::Common::RequestInfo& request_info);
// Indicates whether to export server audit log or not.
bool enableAuditLog();
// Indicates whether the request should be logged based on audit policy
bool shouldAuditThisRequest();
// Indicates whether or not to report TCP Logs.
bool enableTCPServerAccessLog();
// Evaluate Expressions in expressions_ vector and add it in extra_labels.
void evaluateExpressions(std::unordered_map<std::string, std::string>& extra_labels);
// Evaluate Expressions in metrics_expressions_ vector.
void evaluateMetricsExpressions(::Extensions::Stackdriver::Metric::override_map& overrides);
// Evaluates a logging filter. If the returned value is `false`, no log
// entry will be added for the request/connection.
bool evaluateLogFilter();
// Initializes a configured logging filter.
bool initializeLogFilter();
// Cleanup expressions in expressions_ vector.
void cleanupExpressions();
// Cleanup expressions in metrics_expressions_ vector.
void cleanupMetricsExpressions();
// Cleanup any access logging filter expression.
void cleanupLogFilter();
// Config for Stackdriver plugin.
stackdriver::config::v1alpha1::PluginConfig config_;
// Local node info extracted from node metadata.
flatbuffers::DetachedBuffer local_node_info_;
flatbuffers::DetachedBuffer empty_node_info_;
// Indicates the traffic direction relative to this proxy.
::Wasm::Common::TrafficDirection direction_{::Wasm::Common::TrafficDirection::Unspecified};
// Logger records and exports log entries to Stackdriver backend.
std::unique_ptr<::Extensions::Stackdriver::Log::Logger> logger_;
long int tcp_log_entry_timeout_ = kDefaultTcpLogEntryTimeoutNanoseconds;
long int last_log_report_call_nanos_ = 0;
long int log_report_duration_nanos_ = kDefaultLogExportNanoseconds;
bool use_host_header_fallback_;
bool initialized_ = false;
std::unordered_map<uint32_t, std::unique_ptr<StackdriverRootContext::TcpRecordInfo>>
tcp_request_queue_;
// Stores expressions for evaluation for custom access logs.
struct expressionInfo {
uint32_t token;
std::string tag;
std::string expression;
};
std::vector<struct expressionInfo> expressions_;
// Stores expressions for evaluation for metrics.
struct metricsExpressionInfo {
uint32_t token;
std::string metric;
::opencensus::tags::TagKey tag;
std::string expression;
};
std::vector<struct metricsExpressionInfo> metrics_expressions_;
// Stores the reference token for a configured access logging filter
// expression.
uint32_t log_filter_token_;
};
// StackdriverContext is per stream context. It has the same lifetime as
// the request stream itself.
class StackdriverContext : public Context {
public:
StackdriverContext(uint32_t id, RootContext* root)
: Context(id, root), is_tcp_(false), context_id_(id),
is_initialized_(getRootContext()->initialized()) {}
void onLog() override;
FilterStatus onNewConnection() override {
if (!is_initialized_) {
return FilterStatus::Continue;
}
is_tcp_ = true;
getRootContext()->addToTCPRequestQueue(context_id_);
getRootContext()->setConnectionState(context_id_, ::Wasm::Common::TCPConnectionState::Open);
return FilterStatus::Continue;
}
// Called on onData call, so counting the data that is received.
FilterStatus onDownstreamData(size_t size, bool) override {
if (!is_initialized_) {
return FilterStatus::Continue;
}
getRootContext()->incrementReceivedBytes(context_id_, size);
getRootContext()->setConnectionState(context_id_,
::Wasm::Common::TCPConnectionState::Connected);
return FilterStatus::Continue;
}
// Called on onWrite call, so counting the data that is sent.
FilterStatus onUpstreamData(size_t size, bool) override {
if (!is_initialized_) {
return FilterStatus::Continue;
}
getRootContext()->incrementSentBytes(context_id_, size);
getRootContext()->setConnectionState(context_id_,
::Wasm::Common::TCPConnectionState::Connected);
return FilterStatus::Continue;
}
private:
// Gets root Stackdriver context that this stream Stackdriver context
// associated with.
StackdriverRootContext* getRootContext();
bool is_tcp_;
uint32_t context_id_;
const bool is_initialized_;
};
class StackdriverOutboundRootContext : public StackdriverRootContext {
public:
StackdriverOutboundRootContext(uint32_t id, std::string_view root_id)
: StackdriverRootContext(id, root_id) {}
};
class StackdriverInboundRootContext : public StackdriverRootContext {
public:
StackdriverInboundRootContext(uint32_t id, std::string_view root_id)
: StackdriverRootContext(id, root_id) {}
};
static RegisterContextFactory
register_OutboundStackdriverContext(CONTEXT_FACTORY(StackdriverContext),
ROOT_FACTORY(StackdriverOutboundRootContext),
::Extensions::Stackdriver::Common::kOutboundRootContextId);
static RegisterContextFactory
register_InboundStackdriverContext(CONTEXT_FACTORY(StackdriverContext),
ROOT_FACTORY(StackdriverInboundRootContext),
::Extensions::Stackdriver::Common::kInboundRootContextId);
} // namespace Stackdriver
#ifdef NULL_PLUGIN
} // namespace null_plugin
} // namespace proxy_wasm
#endif

View File

@ -0,0 +1,31 @@
/* 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 "include/proxy-wasm/null_plugin.h"
#include "stackdriver.h"
namespace proxy_wasm {
namespace null_plugin {
namespace Stackdriver {
NullPluginRegistry* context_registry_{};
RegisterNullVmPluginFactory register_stackdriver_filter("envoy.wasm.null.stackdriver", []() {
return std::make_unique<NullPlugin>(context_registry_);
});
} // namespace Stackdriver
} // namespace null_plugin
} // namespace proxy_wasm

View File

@ -0,0 +1,75 @@
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: stackdriver-filter
spec:
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_OUTBOUND
listener:
filterChain:
filter:
name: "envoy.http_connection_manager"
subFilter:
name: "envoy.filters.http.router"
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.wasm
config:
config:
root_id: stackdriver_outbound
configuration: |
{}
vm_config:
vm_id: stackdriver_outbound
runtime: envoy.wasm.runtime.null
code:
local: { inline_string: envoy.wasm.null.stackdriver }
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
listener:
filterChain:
filter:
name: "envoy.http_connection_manager"
subFilter:
name: "envoy.filters.http.router"
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.wasm
config:
config:
root_id: stackdriver_inbound
configuration: |
{}
vm_config:
vm_id: stackdriver_inbound
runtime: envoy.wasm.runtime.null
code:
local: { inline_string: envoy.wasm.null.stackdriver }
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: "envoy.http_connection_manager"
subFilter:
name: "envoy.filters.http.router"
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.wasm
config:
config:
root_id: stackdriver_outbound
configuration: |
{"disable_host_header_fallback": true}
vm_config:
vm_id: stackdriver_outbound
runtime: envoy.wasm.runtime.null
code:
local: { inline_string: envoy.wasm.null.stackdriver }

48
go.mod
View File

@ -1,33 +1,35 @@
module istio.io/proxy
go 1.24
go 1.19
require (
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f
github.com/envoyproxy/go-control-plane v0.13.5-0.20250705082150-f8f2cd45490a
github.com/envoyproxy/go-control-plane/envoy v1.32.4
github.com/golang/protobuf v1.5.4
github.com/google/go-cmp v0.7.0
github.com/prometheus/client_model v0.6.1
github.com/prometheus/common v0.46.0
go.opentelemetry.io/proto/otlp v1.1.0
go.starlark.net v0.0.0-20240123142251-f86470692795
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a
google.golang.org/grpc v1.73.0
google.golang.org/protobuf v1.36.6
cloud.google.com/go/logging v1.7.0
cloud.google.com/go/monitoring v1.13.0
cloud.google.com/go/trace v1.9.0
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4
github.com/envoyproxy/go-control-plane v0.11.2-0.20230725211550-11bfe846bcd4
github.com/golang/protobuf v1.5.3
github.com/google/go-cmp v0.5.9
github.com/prometheus/client_model v0.4.0
github.com/prometheus/common v0.38.0
go.opentelemetry.io/proto/otlp v1.0.0
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc
google.golang.org/grpc v1.56.2
google.golang.org/protobuf v1.31.0
gopkg.in/yaml.v2 v2.4.0
sigs.k8s.io/yaml v1.4.0
sigs.k8s.io/yaml v1.3.0
)
require (
cel.dev/expr v0.23.0 // indirect
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
cloud.google.com/go/longrunning v0.4.1 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/envoyproxy/protoc-gen-validate v1.0.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.25.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e // indirect
)

171
go.sum
View File

@ -1,85 +1,112 @@
cel.dev/expr v0.23.0 h1:wUb94w6OYQS4uXraxo9U+wUAs9jT47Xvl4iPgAwM2ss=
cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k=
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I=
cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M=
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
cloud.google.com/go/monitoring v1.13.0 h1:2qsrgXGVoRXpP7otZ14eE1I568zAa92sJSDPyOJvwjM=
cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw=
cloud.google.com/go/trace v1.9.0 h1:olxC0QHC59zgJVALtgqfD9tGk0lfeCP5/AGXL3Px/no=
cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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.13.5-0.20250705082150-f8f2cd45490a h1:k0yPxzI8NWvWx9TKX3ysJabi1XEkrKmutgfIe27WX7M=
github.com/envoyproxy/go-control-plane v0.13.5-0.20250705082150-f8f2cd45490a/go.mod h1:whHrEUXbTAzBJlzd3Gz4us5zEFP1gL6o3LbfA+a/xbg=
github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=
github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.11.2-0.20230725211550-11bfe846bcd4 h1:u4facp1hZe/ZmiAkiEFZmWAbg7s4+a5Z3tkz8Hg6I9w=
github.com/envoyproxy/go-control-plane v0.11.2-0.20230725211550-11bfe846bcd4/go.mod h1:djL+W7LURiPM8Szxc5/47R6qMRulOSkfsDLO1CaGUNM=
github.com/envoyproxy/go-control-plane v0.11.2-0.20230726084335-b501c94cb61e h1:HXKPsHR2TRMWMdbCqwWsWzNqml/+oMQO3zm5LhhoYOo=
github.com/envoyproxy/go-control-plane v0.11.2-0.20230726084335-b501c94cb61e/go.mod h1:djL+W7LURiPM8Szxc5/47R6qMRulOSkfsDLO1CaGUNM=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.0.1 h1:kt9FtLiooDc0vbwTLhdg3dyNX1K9Qwa1EK9LcD4jVUQ=
github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM=
github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y=
github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
go.starlark.net v0.0.0-20240123142251-f86470692795 h1:LmbG8Pq7KDGkglKVn8VpZOZj6vb9b8nKEGcg9l03epM=
go.starlark.net v0.0.0-20240123142251-f86470692795/go.mod h1:LcLNIzVOMp4oV+uusnpk+VU+SzXaJakUuBjoCSWH5dM=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a h1:SGktgSolFCo75dnHJF2yMvnns6jCmHFJ0vE4Vn2JKvQ=
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.38.0 h1:VTQitp6mXTdUoCmDMugDVOJ1opi6ADftKfp/yeqTR/E=
github.com/prometheus/common v0.38.0/go.mod h1:MBXfmBQZrK5XpbCkjofnXs96LD2QQ7fEq4C0xjC/yec=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e h1:Ao9GzfUMPH3zjVfzXG5rlWlk+Q8MXWKwWpwVQE1MXfw=
google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk=
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM=
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI=
google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=

Some files were not shown because too many files have changed in this diff Show More